Note that there are some explanatory texts on larger screens.

plurals
  1. POConverting IEEE 754 floating point in Haskell Word32/64 to and from Haskell Float/Double
    primarykey
    data
    text
    <h1>Question</h1> <p>In Haskell, the <code>base</code> libraries and Hackage packages provide several means of converting binary IEEE-754 floating point data to and from the lifted <code>Float</code> and <code>Double</code> types. However, the accuracy, performance, and portability of these methods are unclear.</p> <p>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?</p> <h1>Approaches</h1> <p>These are the methods I've encountered in existing libraries and online resources.</p> <h2>FFI Marshaling</h2> <p>This is the approach used by the <a href="http://hackage.haskell.org/package/data-binary-ieee754" rel="nofollow noreferrer"><code>data-binary-ieee754</code></a> package. Since <code>Float</code>, <code>Double</code>, <code>Word32</code> and <code>Word64</code> are each instances of <code>Storable</code>, one can <code>poke</code> a value of the source type into an external buffer, and then <code>peek</code> a value of the target type:</p> <pre><code>toFloat :: (F.Storable word, F.Storable float) =&gt; word -&gt; float toFloat word = F.unsafePerformIO $ F.alloca $ \buf -&gt; do F.poke (F.castPtr buf) word F.peek buf </code></pre> <p>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.</p> <h2><code>unsafeCoerce</code></h2> <p>With the same implicit assumption of in-memory IEEE-754 representation, the following code gets the "works on my machine" seal as well:</p> <pre><code>toFloat :: Word32 -&gt; Float toFloat = unsafeCoerce </code></pre> <p>This has the benefit of not performing explicit allocation like the approach above, but the <a href="http://www.haskell.org/ghc/docs/latest/html/libraries/base/Unsafe-Coerce.html" rel="nofollow noreferrer">documentation</a> 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.</p> <h2><code>unsafeCoerce#</code></h2> <p>Stretching the limits of what might be considered "portable":</p> <pre><code>toFloat :: Word -&gt; Float toFloat (W# w) = F# (unsafeCoerce# w) </code></pre> <p>This seems to work, but doesn't seem practical at all since it's limited to the <a href="http://www.haskell.org/ghc/docs/latest/html/libraries/base-4.3.1.0/GHC-Exts.html" rel="nofollow noreferrer"><code>GHC.Exts</code></a> types. It's nice to bypass the lifted types, but that's about all that can be said.</p> <h2><code>encodeFloat</code> and <code>decodeFloat</code></h2> <p>This approach has the nice property of bypassing anything with <code>unsafe</code> in the name, but doesn't seem to get IEEE-754 quite right. A <a href="https://stackoverflow.com/questions/4643135/converting-bytes-to-int64s-floats-doubles-in-haskell/4643578#4643578">previous SO answer</a> to a similar question offers a concise approach, and the <a href="http://hackage.haskell.org/package/ieee754-parser" rel="nofollow noreferrer"><code>ieee754-parser</code></a> package used a more general approach before being deprecated in favor of <code>data-binary-ieee754</code>.</p> <p>There's quite a bit of appeal to having code that needs no implicit assumptions about underlying representation, but these solutions rely on <code>encodeFloat</code> and <code>decodeFloat</code>, which are apparently <a href="http://www.haskell.org/pipermail/haskell-cafe/2008-June/044351.html" rel="nofollow noreferrer">fraught with inconsistencies</a>. I've not yet found a way around these problems.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload