将十进制值转换为32位浮点十六进制
问题描述:
对于我正在处理的一个简单实用程序,我需要一个将给定十进制值转换为32位浮点十六进制值的脚本。例如,我知道1是3F800000,100是42C80000,但是我不知道如何用任何数字返回这些结果。如果有人知道一个简单的公式或甚至复杂的方式去做这件事,请分享。将十进制值转换为32位浮点十六进制
答
我不知道我是否正确地得到了拐角的情况,但无论如何,这里是一些代码:
function floatToIntBits(f) {
var NAN_BITS = 0|0x7FC00000;
var INF_BITS = 0|0x7F800000;
var ZERO_BITS = 0|0x00000000;
var SIGN_BIT = 0|0x80000000;
var EXP_MASK = 0|0x7F800000;
var MANT_MASK = 0|0x007FFFFF;
if (f != f)
return NAN_BITS;
var signBit = (f > 0.0 || (f == 0.0 && Math.pow(f, -1) > 0)) ? 0 : SIGN_BIT;
var fabs = Math.abs(f);
if (fabs == Number.POSITIVE_INFINITY)
return signBit | INF_BITS;
if (fabs == 0.0)
return signBit | ZERO_BITS;
var e = 0, x = f;
while (x != 0.0) {
e++;
x /= 2.0;
}
var exp = e - (1023 + 52);
if (exp >= 127) // XXX: maybe incorrect
return signBit | INF_BITS;
if (exp <= -126) // XXX: maybe incorrect
return signBit | ZERO_BITS;
var ceil = Math.pow(2.0, exp);
//console.log("fabs", fabs, "ceil", ceil);
var mantissa = fabs/ceil * Math.pow(2.0, 24);
if (fabs == ceil) {
mantissa = 0;
} else {
exp--;
}
var expBits = ((exp + 127) << 23) & EXP_MASK;
var mantissaBits = mantissa & MANT_MASK;
//console.log("sign", signBit, "expBits", expBits.toString(16), "mantissaBits", mantissaBits.toString(16));
return signBit | expBits | mantissaBits;
}
function testCase(expected, f) {
var actual = floatToIntBits(f);
if (expected !== actual) {
console.log("expected", expected.toString(16), "actual", actual.toString(16), "f", f);
}
}
testCase(0|0x80000000, -0.0);
testCase(0|0x00000000, 0.0);
testCase(0|0x3F800000, 1.0);
testCase(0|0x42C80000, 100.0);
testCase(0|0x7FC00000, 0.0/0.0);
testCase(0|0x7F800000, 1.0/0.0);
testCase(0|0xFF800000, 1.0/-0.0);
有趣的前瞻性0|0x...
表达是必要的,因为JavaScript将这些文字数为较大的正整数,但应用按位运算符显然会将它们转换为带符号的32位整数。 (比较ECMAScript规范,第8.5节,最后一段)。
更新:以下代码基于上述代码,但它更符合规范的实际措辞。此外,它独立于用于实现JavaScript的特定浮点类型Number
。代码首先将值移至区间[1.0; 2.0),因为这是IEEE 754-1985中为归一化数字提到的表示。这段代码也可以正确处理非规范化的数字,它所使用的所有操作都在IEEE 754-1985中定义,并且是精确的,即它们不会失去精度。
function assert(cond, msg, arg0) {
if (!cond)
console.log("error", msg, arg0);
}
function floatToIntBits(f) {
var NAN_BITS = 0|0x7FC00000;
var INF_BITS = 0|0x7F800000;
var ZERO_BITS = 0|0x00000000;
var SIGN_MASK = 0|0x80000000;
var EXP_MASK = 0|0x7F800000;
var MANT_MASK = 0|0x007FFFFF;
var MANT_MAX = Math.pow(2.0, 23) - 1.0;
if (f != f)
return NAN_BITS;
var hasSign = f < 0.0 || (f == 0.0 && 1.0/f < 0);
var signBits = hasSign ? SIGN_MASK : 0;
var fabs = Math.abs(f);
if (fabs == Number.POSITIVE_INFINITY)
return signBits | INF_BITS;
var exp = 0, x = fabs;
while (x >= 2.0 && exp <= 127) {
exp++;
x /= 2.0;
}
while (x < 1.0 && exp >= -126) {
exp--;
x *= 2.0;
}
assert(x * Math.pow(2.0, exp) == fabs, "fabs");
var biasedExp = exp + 127;
assert(0 <= biasedExp && biasedExp <= 254, biasedExp);
if (biasedExp == 255)
return signBit | INF_BITS;
if (biasedExp == 0) {
assert(0.0 <= x && x < 2.0, "x in [0.0, 1.0)", x);
var mantissa = x * Math.pow(2.0, 23)/2.0;
} else {
assert(1.0 <= x && x < 2.0, "x in [0.5; 1.0)", x);
var mantissa = x * Math.pow(2.0, 23) - Math.pow(2.0, 23);
}
assert(0.0 <= mantissa && mantissa <= MANT_MAX, "mantissa in [0.0, 2^23)", mantissa);
//console.log("number", f, "x", x, "biasedExp", biasedExp, "mantissa", mantissa.toString(16));
var expBits = (biasedExp << 23) & EXP_MASK;
var mantissaBits = mantissa & MANT_MASK;
//console.log("number", f, "sign", signBits.toString(16), "expBits", expBits.toString(16), "mantissaBits", mantissaBits.toString(16));
return signBits | expBits | mantissaBits;
}
function testCase(expected, f) {
var actual = floatToIntBits(f);
if (expected !== actual) {
console.log("error", "number", f, "expected", expected.toString(16), "got", actual.toString(16));
}
}
testCase(0|0xFF800000, 1.0/-0.0); // -Inf
testCase(0|0xBF800000, -1.0);
testCase(0|0x80000000, -0.0);
testCase(0|0x00000000, 0.0);
testCase(0|0x00000001, Math.pow(2.0, -(126 + 23))); // minimum denormalized
testCase(0|0x007FFFFF, Math.pow(2.0, -126) - Math.pow(2.0, -(126 + 23))); // maximum denormalized
testCase(0|0x00800000, Math.pow(2.0, -126)); // minimum normalized float
testCase(0|0x3F800000, 1.0);
testCase(0|0x42C80000, 100.0);
testCase(0|0x7F800000, 1.0/0.0); // Inf
testCase(0|0x7FC00000, 0.0/0.0); // NaN
上面的第一个代码片段缺少对非规格化数字的支持。我也不确定它是否正确处理负数。 – 2010-06-27 14:24:08
请注意:我不应该使用'Math.pow(x,y)'函数,因为它只返回“将* x *提升到* y *的结果的实现相关近似值。 [ECMA-262,15.8.2.13] – 2010-07-24 22:10:56