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).

Answers:

Answer

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.

Answer

A quick addition to DigitalRoss' solution, for those finding this page via Google as I did.

Apart from the edge cases for +/- Infinity and NaN, which I'd love input on, you also need to take into account the sign of the result:

s = a >> 31 ? -1 : 1

You can then include s in the final multiplication to get the correct result.

I think for a little-endian solution you'll also need to reverse the bits in a and b and swap them.

Answer

The new Typed Arrays mechanism allows you to do this (and is probably an ideal mechanism for implementing protocol buffers):

var buffer = new ArrayBuffer(8);
var bytes = new Uint8Array(buffer);
var doubles = new Float64Array(buffer); // not supported in Chrome

bytes[7] = 0x40; // Load the hex string "40 72 50 82 00 00 00 00" 
bytes[6] = 0x72;
bytes[5] = 0x50;
bytes[4] = 0x82;
bytes[3] = 0x00;
bytes[2] = 0x00;
bytes[1] = 0x00;
bytes[0] = 0x00;

my_double = doubles[0];

document.write(my_double);  // 293.03173828125

This assumes a little-endian machine.

Unfortunately Chrome does not have Float64Array, although it does have Float32Array. The above example does work in Firefox 4.0.1.

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.