问题
I'm trying to parse a binary file format in Haskell (Apple's binary property list format), and one of the things required by the format is to treat sequences of bytes as either (a) unsigned 1-, 2-, or 4-byte integers; (b) signed 8-byte integers; (c) 32-bit float
s; and (d) 64-bit double
s. Converting sequences of bytes to unsigned integers is easy, and even dealing with signed integers wouldn't be terrible. But for signed integers, and especially for Float
s and Double
s, I don't really want to implement the logic myself. I've been able to find functions int2Float# :: Int# -> Float#
and int2Double# :: Int# -> Double#
in GHC.Prim, but these don't seem ideal (I don't particularly want to be working with unboxed types). My hope is that there's some way to cast from either a [Word8]
or Word32
s/Word64
s. Are there any functions of type Word32 -> Float
, Word64 -> Double
, Word64 -> Int64
, or similar?
回答1:
If you aren't aware, fromIntegral
converts integrals perfectly well. Also, the binary package and associated data-binary-ieee754 package are very applicable to your problem.
λ> :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
回答2:
Unsafe.Coerce.unsafeCoerce can convert between types, like C++'s reinterpret_cast<>
. Use with caution.
Otherwise, you can implement your own IEEE-754 decoding using 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
At least with my GHC, it doesn't seem possible to create -0
and NaN
using encodeFloat
, but everything else should work.
来源:https://stackoverflow.com/questions/4643135/converting-bytes-to-int64s-floats-doubles-in-haskell