Я пытаюсь проанализировать формат двоичного файла в Haskell (формат списка двоичных свойств Apple), и одна из вещей, требуемых форматом, — обрабатывать последовательности байтов как (а) беззнаковые 1-, 2- или 4- байтовые целые числа; (b) знаковые 8-байтовые целые числа; (c) 32-битные float
s; и (d) 64-битные double
s. Преобразование последовательностей байтов в целые числа без знака выполняется легко, и даже работа с целыми числами со знаком не будет ужасной. Но для целых чисел со знаком и особенно для Float
s и Double
s я действительно не хочу реализовывать логику самостоятельно. Мне удалось найти функции int2Float# :: Int# -> Float#
и int2Double# :: Int# -> Double#
в GHC.Prim, но они не кажутся идеальными (мне особенно не хочется работать с неупакованными типами). Я надеюсь, что есть какой-то способ использовать [Word8]
или Word32
s/Word64
s. Существуют ли какие-либо функции типа Word32 -> Float
, Word64 -> Double
, Word64 -> Int64
или подобные?
Преобразование байтов в Int64s/Floats/Double в Haskell
Ответы (2)
Если вы не в курсе, 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
person
Thomas M. DuBuisson
schedule
10.01.2011
Похоже, что
Data.Binary.IEEE754
эффективно действует unsafePerformIO $ peek . castPtr . poke
, что почти так же безопасно, как unsafeCoerce
. Тем не менее, это полезно — это означает, что -0
и NaN
на самом деле корректно передаются туда-обратно.
- person ephemient; 10.01.2011
И новичкам не нужно знать/непосредственно использовать
unsafeCoerce
.
- person Thomas M. DuBuisson; 10.01.2011
Я не знал, что
fromIntegral
будет работать с типами с фиксированной шириной — мне нужно было это проверить.
- person Antal Spector-Zabusky; 10.01.2011
Вдохновленный этим вопросом, я выпустил на Hackage пакет, который реализует переинтерпретацию приведения типов и старается быть в курсе самой быстрой из известных реализаций: hackage.haskell.org/package/reinterpret-cast
- person nh2; 30.04.2014
Unsafe.Coerce.unsafeCoerce
может конвертировать между типами, например 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
, но все остальное должно работать.
person
ephemient
schedule
10.01.2011
unsafeCoerce
не подходит для целочисленного преобразования с плавающей запятой ‹-›, см. and-from-haskell-floa">этот вопрос и здесь.
- person nh2; 30.04.2014