sbv-14.8: Data/SBV/Compilers/C/Real.hs
-----------------------------------------------------------------------------
-- |
-- Module : Data.SBV.Compilers.C.Real
-- Copyright : (c) Levent Erkok
-- License : BSD3
-- Maintainer: erkokl@gmail.com
-- Stability : experimental
--
-- Safe flooring of native real representations to mapped integers.
-----------------------------------------------------------------------------
{-# OPTIONS_GHC -Wall -Werror #-}
module Data.SBV.Compilers.C.Real (mappedRealFloorWidth, mappedRealFloorCall, mappedRealFloorRuntime) where
import Data.Maybe (mapMaybe)
import Text.PrettyPrint.HughesPJ
import qualified Text.PrettyPrint.HughesPJ as P ((<>))
import Data.SBV.Compilers.CodeGen (CgConfig(..))
import Data.SBV.Core.Data
-- | Identify flooring from an explicitly native real to a mapped integer.
-- Exact GMP representations are handled by their own conversion paths.
mappedRealFloorWidth :: CgConfig -> Op -> Maybe Int
mappedRealFloorWidth cfg (KindCast KReal KUnbounded)
| Just _ <- cgReal cfg = cgInteger cfg
mappedRealFloorWidth _ _ = Nothing
-- | Call the width-specific helper. Promotion to long double is exact for
-- the native float and double mappings and preserves long-double precision.
mappedRealFloorCall :: Int -> Doc -> Doc
mappedRealFloorCall width value = text (mappedRealFloorName width) P.<> parens value
-- | Emit the helper only when a program actually floors a mapped real.
-- Reduce the magnitude modulo a power of two before any integer cast. Split
-- 64-bit residues into 32-bit halves so even a binary64 long double never
-- needs to represent UINT64_MAX or a rounded signed upper bound.
-- Non-finite real representations have no mathematical floor and fail fast.
mappedRealFloorRuntime :: CgConfig -> [(SV, SBVExpr)] -> Doc
mappedRealFloorRuntime cfg assignments = case mapMaybe (\(_, SBVApp operation _) -> mappedRealFloorWidth cfg operation) assignments of
[] -> empty
width:_ -> text . unlines $
[ "/* Floor native reals, then retain the mapped integer's low bits. */"
, "static " ++ signedType ++ " " ++ mappedRealFloorName width ++ "(long double value)"
, "{"
, " if (!isfinite(value)) {"
, " fputs(\"SBV->C: Cannot floor a non-finite mapped SReal to SInteger.\\n\", stderr);"
, " abort();"
, " }"
, " const long double integral = floorl(value);"
, " const long double residue = fmodl(fabsl(integral), 0x1p" ++ show width ++ "L);"
, " const uint64_t high = (uint64_t) (residue / 0x1p32L);"
, " const uint64_t low = (uint64_t) (residue - (long double) high * 0x1p32L);"
, " uint64_t bits = (high << 32) | low;"
, " if (signbit(integral)) bits = UINT64_C(0) - bits;"
, " const " ++ unsignedType ++ " raw = (" ++ unsignedType ++ ") bits;"
, " " ++ signedType ++ " result; memcpy(&result, &raw, sizeof result); return result;"
, "}"
, ""
]
where signedType = "SInt" ++ show width
unsignedType = "SWord" ++ show width
-- | Name the private helper by its mapped integer width.
mappedRealFloorName :: Int -> String
mappedRealFloorName width = "sbv_real_floor_to_s" ++ show width