BAN IP
BAN IP

Reputation: 93

Javascript - Convert hex to IEEE-754 64-bit double precision

I'm trying to convert the following hex string: "40934B999999999A" to 1234.9 (float-64).

I looked for several solutions posted on the internet, but most of them were only codes that gave answers different from what I needed, such as changing hex to float type.

Because I am using a legacy solution, I am in an environment where I cannot use es6 or higher syntax (eg DataView or Int16Array).

How can I get a javascript function that gives me the answer I need?

Thank you

Upvotes: 0

Views: 1348

Answers (2)

Řrřola
Řrřola

Reputation: 6307

The following ES1-compatible function exactly converts a hexadecimal string into a 64-bit floating-point number.
It can return only numbers representable in JavaScript, so NaNs will lose their payload.

function hex_to_number(str) {
  // Pad the string with zeroes to 16 characters.
  // You can omit this if you control your inputs.
  str = (str + "0000000000000000").slice(0,16);

  // Split into bits: sign (1), exponent (11), significand (52).
  var sign_and_exponent_bits = parseInt(str.slice(0,3), 16);
  var sign = sign_and_exponent_bits >= 0x800 ? -1 : +1;
  var exponent_bits = sign_and_exponent_bits & ((1<<11) - 1);
  var significand_bits = parseInt(str.slice(3,16), 16);

  // Classify the floating-point value.
  if (exponent_bits == 0x7FF)  // infinity | not a number
    return significand_bits == 0 ? sign * Number.POSITIVE_INFINITY : Number.NaN;
  else if (exponent_bits == 0)  // zero | subnormal number
    return sign * Math.pow(2, 1-1023-52) * significand_bits;
  else  // normal number
    return sign * Math.pow(2, exponent_bits-1023-52) * (Math.pow(2, 52) + significand_bits);
}

For your example "40934B999999999A":

  • sign = +1
  • exponent_bits = 0x409: it's a normalized number
  • significand_bits = 0x34B999999999A
  • the result is 2⁻⁴² ⋅ 0x134B999999999A, which is the closest float64 to 1234.9 (exactly 1234.90000000000009094947017729282379150390625).

Edit: If you can't trust the implementation of Math.pow(2, n), you can compute it with the following function:

function ldexp(x, n) {  // compute 2^n * x, assume n is an integer
  if (!isFinite(x) || x == 0) return x;
  if (n < -2098) return x * 0;
  if (n > 2097) return x * Number.POSITIVE_INFINITY;

  // Make negative exponents positive.
  var p = 2;
  if (n < 0) { n = -n; p = 0.5; }

  // Compute 2^n by binary exponentiation.
  for (var i=1; ; i<<=1) {
    if (n & i) x *= p;
    if (i == 512) break;
    p *= p;
  }
  // Do the remaining bits manually because 2^1024 overflows.
  if (n & 1024) { x *= p; x *= p; }
  if (n & 2048) { x *= p; x *= p; x *= p; x *= p; }
  return x;
}

Upvotes: 2

alias
alias

Reputation: 30428

The encoding you have is the standard IEEE754 layout for double-precision numbers. Here's the detailed layout:

  DECODED = 1234.9 :: Double
                  6    5          4         3         2         1         0
                  3 21098765432 1098765432109876543210987654321098765432109876543210
                  S ----E11---- ------------------------S52-------------------------
   Binary layout: 0 10000001001 0011010010111001100110011001100110011001100110011010
      Hex layout: 4093 4B99 9999 999A
       Precision: Double
            Sign: Positive
        Exponent: 10 (Stored: 1033, Bias: 1023)
  Classification: FP_NORMAL
          Binary: 0b1.001101001011100110011001100110011001100110011001101p+10
           Octal: 0o2.32271463146314632p+9
         Decimal: 1234.9
             Hex: 0x4.D2E6666666668p+8

If you're using node.js, then you can use https://nodejs.org/docs/latest/api/buffer.html#bufreaddoublebeoffset to read and https://nodejs.org/docs/latest/api/buffer.html#bufwritedoublebevalue-offset to write such values.

If you have to code this up yourself, then https://en.wikipedia.org/wiki/Double-precision_floating-point_format contains all the details you need.

Upvotes: 0

Related Questions