Search code examples
javascriptfloating-pointhex64-bitieee-754

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


Solution

  • 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;
    }