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