Haskell中浮点数的十六进制表示

8
我想将Haskell Float转换为包含标准IEEE格式中32位十六进制表示的字符串。我似乎找不到一个能够帮我完成这个任务的软件包。有人知道吗?
我注意到GHC.Float提供了一个函数来将Float分解为其带符号基数和指数(decodeFloat),但是这会为基数和指数分别提供14位和8位的十六进制数字,这比32位还要多得多。这似乎没有什么帮助。
如果有更简单的方法可以做到这一点,而我却没有看到,请告诉我。

Haskell中的浮点数只有4个字节(32位)吗?这似乎不足以给您提供14位数字尾数和8位指数。 - pavium
4个回答

6

float-ieee包是纯Haskell-98编写的,但非常耗费CPU。如果您需要多次执行此操作,并且不介意使用GHC,则可以使用以下代码提取将Double的IEEE表示形式作为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)

你可以为 FloatWord32 编写类似的代码。

从技术上讲,这也假定 Double 在硬件上以 IEEE 格式表示,但这可能适用于 GHC 运行的每个平台。您还需要注意有关机器表示的 Word64 的字节序问题。 - Reid Barton

4
在Hackage上有一个float-ieee包,您可以使用它进行操作。请访问http://hackage.haskell.org/package/data-binary-ieee754。该包将打印出表示传入的浮点数的32位ieee754字符串值。
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

这将“Pi”打印为“@I”。这是另一种十六进制表示吗? - Jeremy
这不是十六进制。它只是一个字节串。您需要使用不同的库/函数将字节串值打印为十六进制。 - sclv

2

根据您的个人口味,有几种不同的方法可以实现这一点。使用像Don提到的库可能是最好的选择,否则您可以尝试以下类似的方法:

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)

有三种不同的方式。最后一种是GHC特有的。其他两种方法可能适用于其他Haskell编译器,但有点依赖底层实现,所以很难保证。


1

我想你意外地解码了一个 Double 而不是一个 Float。这就是为什么它似乎不合适的原因。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接