Шестнадцатеричное представление чисел в Хаскеле - PullRequest
7 голосов
/ 16 февраля 2010

Я хочу преобразовать число с плавающей запятой в Haskell в строку, содержащую 32-разрядное шестнадцатеричное представление числа с плавающей запятой в стандартном формате IEEE. Я не могу найти пакет, который сделает это для меня. Кто-нибудь знает об этом?

Я заметил, что GHC.Float предлагает функцию для разложения Float на его подписанные основание и экспоненту (decodeFloat), но это обеспечивает 14- и 8-значное шестнадцатеричное число для основания и экспоненты, соответственно, что требует до гораздо более 32 бит. Кажется, это не помогает.

Если есть более простой способ сделать это, которого я не вижу, пожалуйста, дайте мне знать.

Ответы [ 4 ]

5 голосов
/ 16 февраля 2010

Пакет float-ieee является чистым Haskell-98, но сильно загружает процессор. Если вам понадобится делать это много раз, и вы не против того, чтобы быть специфичным для GHC, то вы используете такой код, который извлекает представление IEEE для Double как 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)

Вы можете кодировать что-то подобное для Float и Word32.

4 голосов
/ 16 февраля 2010

Как насчет пакета float-ieee на Hackage? 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
2 голосов
/ 21 сентября 2010

Есть несколько разных способов сделать это, в зависимости от вашего вкуса. Использование библиотеки, о которой упоминал Дон, вероятно, является наилучшим вариантом, в противном случае вы можете попробовать что-то вроде этого:

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 голос
/ 16 февраля 2010

Я думаю, вы случайно расшифровали Double вместо Float. Вот почему он не подходит.

...