Search code examples
javascriptdoublehexieee-754

Convert a string with a hex representation of an IEEE-754 double into JavaScript numeric variable


Suppose I have a hex number "4072508200000000" and I want the floating point number that it represents (293.03173828125000) in IEEE-754 double format to be put into a JavaScript variable.

I can think of a way that uses some masking and a call to pow(), but is there a simpler solution?

A client-side solution is needed.

This may help. It's a website that lets you enter a hex encoding of an IEEE-754 and get an analysis of mantissa and exponent.

http://babbage.cs.qc.edu/IEEE-754/64bit.html

Because people always tend to ask "why?," here's why: I'm trying to fill out an existing but incomplete implementation of Google's Procol Buffers (protobuf).


Solution

  • I don't know of a good way. It certainly can be done the hard way, here is a single-precision example totally within JavaScript:

    js> a = 0x41973333
    1100428083
    js> (a & 0x7fffff | 0x800000) * 1.0 / Math.pow(2,23) * Math.pow(2,  ((a>>23 & 0xff) - 127))
    18.899999618530273
    

    A production implementation should consider that most of the fields have magic values, typically implemented by specifying a special interpretation for what would have been the largest or smallest. So, detect NaNs and infinities. The above example should be checking for negatives. (a & 0x80000000)

    Update: Ok, I've got it for double's, too. You can't directly extend the above technique because the internal JS representation is a double, and so by its definition it can handle at best a bit string of length 52, and it can't shift by more than 32 at all.

    Ok, to do double you first chop off as a string the low 8 digits or 32 bits; process them with a separate object. Then:

    js> a = 0x40725082      
    1081233538
    js> (a & 0xfffff | 0x100000) * 1.0 / Math.pow(2, 52 - 32) * Math.pow(2, ((a >> 52 - 32 & 0x7ff) - 1023))
    293.03173828125
    js> 
    

    I kept the above example because it's from the OP. A harder case is when the low 32-bits have a value. Here is the conversion of 0x40725082deadbeef, a full-precision double:

    js> a = 0x40725082
    1081233538
    js> b = 0xdeadbeef
    3735928559
    js> e = (a >> 52 - 32 & 0x7ff) - 1023
    8
    js> (a & 0xfffff | 0x100000) * 1.0 / Math.pow(2,52-32) * Math.pow(2, e) +          
         b * 1.0 / Math.pow(2, 52) * Math.pow(2, e)
    293.0319506442019
    js> 
    

    There are some obvious subexpressions you can factor out but I've left it this way so you can see how it relates to the format.