I want to convert a Haskell Float to a String that contains the 32-bit hexadecimal representation of the float in standard IEEE format.开发者_JAVA百科 I can't seem to find a package that will do this for me. Does anybody know of one?
I've noticed that GHC.Float offers a function to decompose a Float into its signed base and exponent (decodeFloat), but this provides a 14- and 8-digit hex number for the base and exponent, respectively, which takes up much more than 32 bits. This doesn't seem to help.
If there's an easier way to do this that I'm not seeing, please let me know.
The float-ieee package is pure Haskell-98, but very CPU intensive. If you are going to need to do this many times, and don't mind being GHC specific, then you use code like this, which extracts the IEEE representation of a Double
as a Word64
:
import GHC.Prim
import GHC.Types
import GHC.Word
encodeIEEEDouble :: Double -> Word64
encodeIEEEDouble (D# x) = W64# (unsafeCoerce# x)
decodeIEEEDouble :: Word64 -> Double
decodeIEEEDouble (W64# x) = D# (unsafeCoerce# x)
You can code something similar for Float
and Word32
.
How about the float-ieee package on Hackage? http://hackage.haskell.org/package/data-binary-ieee754
Will print a 32 bit ieee754 string value representing the float passed in.
import Data.Binary.Put
import Data.Binary.IEEE754
import qualified Data.ByteString.Lazy.Char8 as S
main = do
let s = runPut $ putFloat32be pi
S.putStrLn s
There are a few different ways you can do it, depending on your taste. Using a library like Don mentioned is probably the best option, otherwise you can try something along the lines of these:
doubleToBytes :: Double -> [Int]
doubleToBytes d
= runST (do
arr <- newArray_ ((0::Int),7)
writeArray arr 0 d
arr <- castDoubleToWord8Array arr
i0 <- readArray arr 0
i1 <- readArray arr 1
i2 <- readArray arr 2
i3 <- readArray arr 3
i4 <- readArray arr 4
i5 <- readArray arr 5
i6 <- readArray arr 6
i7 <- readArray arr 7
return (map fromIntegral [i0,i1,i2,i3,i4,i5,i6,i7])
)
-- | Store to array and read out individual bytes of array
dToStr :: Double -> String
dToStr d
= let bs = doubleToBytes d
hex d' = case showHex d' "" of
[] -> error "dToStr: too few hex digits for float"
[x] -> ['0',x]
[x,y] -> [x,y]
_ -> error "dToStr: too many hex digits for float"
str = map toUpper $ concat . fixEndian . (map hex) $ bs
in "0x" ++ str
-- | Create pointer to Double and cast pointer to Word64, then read out
dToStr2 :: Double -> IO String
dToStr2 f = do
fptr <- newStablePtr f
let pptr = castStablePtrToPtr fptr
let wptr = (castPtrToStablePtr pptr)::(StablePtr Word64)
w <- deRefStablePtr wptr
let s = showHex w ""
return ("0x" ++ (map toUpper s))
-- | Use GHC specific primitive operations
dToStr3 :: Double -> String
dToStr3 (D# f) = "0x" ++ (map toUpper $ showHex w "")
where w = W64# (unsafeCoerce# f)
Three different ways. Last is GHC specific. Other two may work with other Haskell compilers but are relying a little on the underlying implementation so hard to guarantee.
I think you accidentally decoded a Double
instead of a Float
. That's why it doesn't seem to fit.
精彩评论