Преобразование байтов в Int64s / Floats / Doubles в Haskell - PullRequest
4 голосов
/ 10 января 2011

Я пытаюсь проанализировать двоичный формат файла в Haskell (формат списка двоичных свойств Apple), и одна из вещей, требуемых форматом, состоит в том, чтобы обрабатывать последовательности байтов как (a) без знака 1-, 2-,или 4-байтовые целые числа;(б) 8-байтовые целые числа со знаком;(в) 32-битный float с;и (d) 64-битный double с.Конвертировать последовательности байтов в целые числа без знака легко, и даже работа со знаковыми целыми числами не будет ужасной .Но для целых чисел со знаком и , особенно для Float с и Double с, я не хочу сам реализовывать логику.Мне удалось найти функции int2Float# :: Int# -> Float# и int2Double# :: Int# -> Double# в GHC.Prim , но они не кажутся идеальными (я не особо хочу работать с распакованными типами).Я надеюсь, что есть какой-то способ кастовать из [Word8] или Word32 s / Word64 s.Существуют ли функции типа Word32 -> Float, Word64 -> Double, Word64 -> Int64 или аналогичные?

Ответы [ 2 ]

5 голосов
/ 10 января 2011

Если вы не знаете, 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 голос
/ 10 января 2011

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, но все остальное должно работать.

...