canontra-0.1.0.0: src/Canontra/Canonical/Float.hs
{- |
Module : Canontra.Canonical.Float
Description : IEEE-754 64-bit canonical floating-point normalizer and encoder.
Ensures deterministic cross-platform binary representations of floating-point numbers:
- Normalizes negative zero (-0.0) to positive zero (+0.0)
- Collapses all NaN representations to the canonical quiet NaN (0x7FF8000000000000)
- Serializes as big-endian 64-bit words.
-}
module Canontra.Canonical.Float
( canonicalizeFloatWord
, canonicalizeFloat
, encodeCanonicalFloat
) where
import Data.Bits ((.&.), shiftR)
import qualified Data.ByteString as BS
import Data.Word (Word64, Word8)
import GHC.Float (castDoubleToWord64)
-- | Convert a Double to a canonicalized IEEE-754 64-bit Word64.
canonicalizeFloatWord :: Double -> Word64
canonicalizeFloatWord d
| isNaN d = 0x7FF8000000000000 -- Canonical quiet NaN
| d == 0.0 = 0 -- Normalizes -0.0 to +0.0
| otherwise = castDoubleToWord64 d
-- | Canonicalize a Double value (maps -0.0 to +0.0).
canonicalizeFloat :: Double -> Double
canonicalizeFloat d
| d == 0.0 = 0.0
| otherwise = d
-- | Encode a Double as an 8-byte big-endian ByteString.
encodeCanonicalFloat :: Double -> BS.ByteString
encodeCanonicalFloat d =
let w = canonicalizeFloatWord d
in BS.pack
[ fromIntegral (shiftR w 56 .&. 0xFF) :: Word8
, fromIntegral (shiftR w 48 .&. 0xFF) :: Word8
, fromIntegral (shiftR w 40 .&. 0xFF) :: Word8
, fromIntegral (shiftR w 32 .&. 0xFF) :: Word8
, fromIntegral (shiftR w 24 .&. 0xFF) :: Word8
, fromIntegral (shiftR w 16 .&. 0xFF) :: Word8
, fromIntegral (shiftR w 8 .&. 0xFF) :: Word8
, fromIntegral (w .&. 0xFF) :: Word8
]