Ich versuche, ein binäres Dateiformat in Haskell (Apples binärer Eigenschaftsliste Format) zu analysieren, und eines der Dinge, die durch das Format erforderlich ist Byte-Sequenzen zu behandeln, als entweder (a) vorzeichenlose 1-, 2- oder 4-Byte-Ganzzahlen; (b) vorzeichenbehaftete 8-Byte-Ganzzahlen; (c) 32-Bit float
s; und (d) 64-Bit double
s. Das Konvertieren von Sequenzen von Bytes in Ganzzahlen ohne Vorzeichen ist einfach, und selbst der Umgang mit Ganzzahlen mit Vorzeichen wäre nicht schrecklich. Aber für signierte ganze Zahlen, und besonders für Float
s und Double
s, ich will nicht wirklich die Logik selbst implementieren. Ich habe in der Lage Funktionen int2Float# :: Int# -> Float#
und int2Double# :: Int# -> Double#
in GHC.Prim zu finden, aber diese nicht ideal erscheinen (ich will nicht besonders mit unboxed Typen zu arbeiten). Meine Hoffnung ist, dass es einen Weg gibt, entweder von [Word8]
oder Word32
s/Word64
s zu werfen. Gibt es Funktionen vom Typ Word32 -> Float
, Word64 -> Double
, Word64 -> Int64
oder ähnlich?Konvertieren von Bytes zu Int64s/Floats/Double in Haskell
Antwort
Wenn Sie nicht wissen, wandelt fromIntegral
sehr gut Integrale. Auch das binary Paket und das zugehörige data-binary-ieee754 Paket sind sehr gut auf Ihr Problem anwendbar.
Unsafe.Coerce.unsafeCoerce
kann zwischen Typen konvertieren, wie C++ reinterpret_cast<>
. Verwenden Sie mit Vorsicht.
Andernfalls können Sie Ihre eigene IEEE-754 Decodierung mit RealFloat
implementieren.
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
Mindestens mit meinem GHC, ist es nicht möglich sein -0
und NaN
erstellen encodeFloat
verwenden, aber alles andere sollte funktionieren.
'unsafeCoerce' ist nicht korrekt für floating <-> integrale Konvertierung, siehe [diese Frage] (http://stackoverflow.com/questions/6976684/converting-ieee-754-floating-point-in-haskell-word32-64 -to-and-from-haskell-floa) und [hier] (https://ghc.haskell.org/trac/ghc/ticket/2209). – nh2
Sieht so aus, als ob 'Data.Binary.IEEE754' effektiv 'unsafePerformIO $ peek' zeigt. BesetzungPtr. poke', was ungefähr so sicher ist wie 'unsafeCoerce'. Trotzdem nützlich - es bedeutet, dass "-0" und "NaN" tatsächlich richtig herumlaufen. – ephemient
Und es erfordert keine Anfänger zu wissen/direkt verwenden 'unsafeCoerce'. –
Ich wusste nicht, dass 'fromIntegral' mit den Typen mit fester Breite funktionieren würde - das hätte ich testen sollen. –