在Haskell中将字节转换为Int64s / Floats / Doubles

时间:2011-01-10 02:01:05

标签: haskell bits

我正在尝试解析Haskell中的二进制文件格式(Apple的二进制属性列表格式),格式所需的一件事就是将字节序列视为(a)无符号1-,2-,或4字节整数; (b)签署8字节整数; (c)32位float s; (d)64位double s。将字节序列转换为无符号整数很容易,甚至处理有符号整数也不会糟糕。但对于有符号整数,以及FloatDouble尤其,我真的不想自己实现逻辑。我已经能够在GHC.Prim中找到函数int2Float# :: Int# -> Float#int2Double# :: Int# -> Double#,但这些似乎并不理想(我不特别想要使用未装箱的类型)。我希望有一些方法可以从[Word8]Word32 s / Word64投射。是否有Word32 -> FloatWord64 -> DoubleWord64 -> Int64或类似的任何功能?

2 个答案:

答案 0 :(得分:5)

如果您不知道,fromIntegral可以很好地转换积分。此外,binary包和相关的data-binary-ieee754包非常适用于您的问题。

λ> :set -XOverloadedStrings
λ> import           Data.Binary.Get       (runGet)
λ> import qualified Data.Binary.IEEE754   as I
λ> runGet I.getFloat32le "\STX\SOH\SOH\SOH"
2.369428e-38
λ> runGet I.getFloat32le "\STX\SOH\SOH\SOHtrailing characters are ignored"
2.369428e-38
λ> runGet I.getFloat32le "\STX\SOH\SOH" -- remember to use `catch`:
*** Exception: Data.Binary.Get.runGet at position 0: not enough bytes
CallStack (from HasCallStack):
  error, called at libraries/binary/src/Data/Binary/Get.hs:351:5 in binary-0.8.5.1:Data.Binary.Get

答案 1 :(得分:1)

Unsafe.Coerce.unsafeCoerce可以在类型之间进行转换,例如C ++的reinterpret_cast<>。请谨慎使用。

否则,您可以使用IEEE-754实现自己的RealFloat解码。

bitsAsIEEE754 :: (Bits a, Integral a, RealFloat b) => a -> b
bitsAsIEEE754 word =
    assert (floatRadix float == 2) $
    assert (bitSize word == 1 + es + ms) $
    assert (1 `shiftL` es == maxE - minE + 3) $
    float
  where
    ms = floatDigits float - 1
    (minE, maxE) = floatRange float
    es = length $ takeWhile (< maxE - minE + 2) $ iterate (* 2) 1
    sgn = if testBit word (ms + es) then negate else id
    e = fromIntegral $ word `shiftR` ms .&. (1 `shiftL` es - 1)
    nor = if e == 0 then id else flip setBit ms
    m = sgn . toInteger . nor $ word .&. (1 `shiftL` ms - 1)
    float = encodeFloat m $ max minE (e + minE - 1) - ms - 1

至少在我的GHC中,似乎无法使用-0创建NaNencodeFloat,但其他一切都应该有效。