Javascript - 将十六进制转换为 IEEE-754 64 位双精度
Javascript - Convert hex to IEEE-754 64-bit double precision
我正在尝试将以下十六进制字符串:"40934B999999999A" 转换为 1234.9 (float-64)。
我在网上找了几个解决方案,但大部分都是代码,答案和我需要的不一样,比如把hex改成float类型
因为我使用的是遗留解决方案,所以我处于无法使用 es6 或更高语法(例如 DataView 或 Int16Array)的环境中。
我怎样才能得到一个 javascript 函数来给出我需要的答案?
谢谢
您的编码是双精度数字的标准 IEEE754 布局。详细布局如下:
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
如果您使用 node.js,那么您可以使用 https://nodejs.org/docs/latest/api/buffer.html#bufreaddoublebeoffset
读取和 https://nodejs.org/docs/latest/api/buffer.html#bufwritedoublebevalue-offset 写入这些值。
如果您必须自己编写代码,那么 https://en.wikipedia.org/wiki/Double-precision_floating-point_format 包含您需要的所有详细信息。
下面的 ES1 兼容函数准确地将十六进制字符串转换为 64 位浮点数。
它可以 return 只能表示 JavaScript 中的数字,因此 NaN 将丢失其有效负载。
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);
}
例如“40934B999999999A”:
- 符号 = +1
- exponent_bits = 0x409: 这是一个标准化数字
- significand_bits = 0x34B999999999A
- 结果是 2⁻⁴² ⋅ 0x134B999999999A,这是最接近 1234.9 的 float64(正好是 1234.90000000000009094947017729282379150390625)。
编辑:如果你不相信Math.pow(2, n)
的实现,你可以用下面的函数计算它:
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;
}
我正在尝试将以下十六进制字符串:"40934B999999999A" 转换为 1234.9 (float-64)。
我在网上找了几个解决方案,但大部分都是代码,答案和我需要的不一样,比如把hex改成float类型
因为我使用的是遗留解决方案,所以我处于无法使用 es6 或更高语法(例如 DataView 或 Int16Array)的环境中。
我怎样才能得到一个 javascript 函数来给出我需要的答案?
谢谢
您的编码是双精度数字的标准 IEEE754 布局。详细布局如下:
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
如果您使用 node.js,那么您可以使用 https://nodejs.org/docs/latest/api/buffer.html#bufreaddoublebeoffset 读取和 https://nodejs.org/docs/latest/api/buffer.html#bufwritedoublebevalue-offset 写入这些值。
如果您必须自己编写代码,那么 https://en.wikipedia.org/wiki/Double-precision_floating-point_format 包含您需要的所有详细信息。
下面的 ES1 兼容函数准确地将十六进制字符串转换为 64 位浮点数。
它可以 return 只能表示 JavaScript 中的数字,因此 NaN 将丢失其有效负载。
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);
}
例如“40934B999999999A”:
- 符号 = +1
- exponent_bits = 0x409: 这是一个标准化数字
- significand_bits = 0x34B999999999A
- 结果是 2⁻⁴² ⋅ 0x134B999999999A,这是最接近 1234.9 的 float64(正好是 1234.90000000000009094947017729282379150390625)。
编辑:如果你不相信Math.pow(2, n)
的实现,你可以用下面的函数计算它:
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;
}