Reputation: 93
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
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":
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
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