Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • Home
  • SEARCH
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 6327677
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 24, 20262026-05-24T17:19:22+00:00 2026-05-24T17:19:22+00:00

Question In Haskell, the base libraries and Hackage packages provide several means of converting

  • 0

Question

In Haskell, the base libraries and Hackage packages provide several means of converting binary IEEE-754 floating point data to and from the lifted Float and Double types. However, the accuracy, performance, and portability of these methods are unclear.

For a GHC-targeted library intended to (de)serialize a binary format across platforms, what is the best approach for handling IEEE-754 floating point data?

Approaches

These are the methods I’ve encountered in existing libraries and online resources.

FFI Marshaling

This is the approach used by the data-binary-ieee754 package. Since Float, Double, Word32 and Word64 are each instances of Storable, one can poke a value of the source type into an external buffer, and then peek a value of the target type:

toFloat :: (F.Storable word, F.Storable float) => word -> float
toFloat word = F.unsafePerformIO $ F.alloca $ \buf -> do
    F.poke (F.castPtr buf) word
    F.peek buf

On my machine this works, but I cringe to see allocation being performed just to accomplish the coercion. Also, although not unique to this solution, there’s an implicit assumption here that IEEE-754 is actually the in-memory representation. The tests accompanying the package give it the “works on my machine” seal of approval, but this is not ideal.

unsafeCoerce

With the same implicit assumption of in-memory IEEE-754 representation, the following code gets the “works on my machine” seal as well:

toFloat :: Word32 -> Float
toFloat = unsafeCoerce

This has the benefit of not performing explicit allocation like the approach above, but the documentation says “it is your responsibility to ensure that the old and new types have identical internal representations”. That implicit assumption is still doing all the work, and is even more strenuous when dealing with lifted types.

unsafeCoerce#

Stretching the limits of what might be considered “portable”:

toFloat :: Word -> Float
toFloat (W# w) = F# (unsafeCoerce# w)

This seems to work, but doesn’t seem practical at all since it’s limited to the GHC.Exts types. It’s nice to bypass the lifted types, but that’s about all that can be said.

encodeFloat and decodeFloat

This approach has the nice property of bypassing anything with unsafe in the name, but doesn’t seem to get IEEE-754 quite right. A previous SO answer to a similar question offers a concise approach, and the ieee754-parser package used a more general approach before being deprecated in favor of data-binary-ieee754.

There’s quite a bit of appeal to having code that needs no implicit assumptions about underlying representation, but these solutions rely on encodeFloat and decodeFloat, which are apparently fraught with inconsistencies. I’ve not yet found a way around these problems.

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-24T17:19:22+00:00Added an answer on May 24, 2026 at 5:19 pm

    Simon Marlow mentions another approach in GHC bug 2209 (also linked to from Bryan O’Sullivan’s answer)

    You can achieve the desired effect using castSTUArray, incidentally
    (this is the way we do it in GHC).

    I’ve used this option in some of my libraries in order to avoid the unsafePerformIO required for the FFI marshalling method.

    {-# LANGUAGE FlexibleContexts #-}
    
    import Data.Word (Word32, Word64)
    import Data.Array.ST (newArray, castSTUArray, readArray, MArray, STUArray)
    import GHC.ST (runST, ST)
    
    wordToFloat :: Word32 -> Float
    wordToFloat x = runST (cast x)
    
    floatToWord :: Float -> Word32
    floatToWord x = runST (cast x)
    
    wordToDouble :: Word64 -> Double
    wordToDouble x = runST (cast x)
    
    doubleToWord :: Double -> Word64
    doubleToWord x = runST (cast x)
    
    {-# INLINE cast #-}
    cast :: (MArray (STUArray s) a (ST s),
             MArray (STUArray s) b (ST s)) => a -> ST s b
    cast x = newArray (0 :: Int, 0) x >>= castSTUArray >>= flip readArray 0
    

    I inlined the cast function because doing so causes GHC to generate much tighter core. After inlining, wordToFloat is translated to a call to runSTRep and three primops (newByteArray#, writeWord32Array#, readFloatArray#).

    I’m not sure what performance is like compared to the FFI marshalling method, but just for fun I compared the core generated by both options.

    Doing FFI marshalling is a fair bit more complicated in this regard. It calls unsafeDupablePerformIO and 7 primops (noDuplicate#, newAlignedPinnedByteArray#, unsafeFreezeByteArray#, byteArrayContents#, writeWord32OffAddr#, readFloatOffAddr#, touch#).

    I’ve only just started learning how to analyse core, perhaps someone with more experience can comment on the cost of these operations?

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

There are several packages available for the usage of regular expressions in Haskell (e.g.
A bit of a neophyte haskell question, but I came across this example in
This question is for the people who know both Haskell (or any other functional
complete noob to Haskell here with probably an even noobier question. I'm trying to
i have a question regarding this Haskell code: module Queue (Queue, emptyQueue, queueEmpty, enqueue,
This question here is related to Haskell Input Return Tuple I wonder how we
Inspired by the recent question about 2d grids in Haskell, I'm wondering if it
I've been working on Question 67A of 99 Haskell Questions . The question is
I'm a haskell newbie and I couldn't find an answer to this question. Can
I am new to both Haskell and programming. My question about binding in pattern-matched,

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.