将十进制值转换为32位浮点数十六进制值

3

我正在开发一个简单的实用程序,需要一个将给定的十进制值转换为32位浮点十六进制值的脚本。例如,我知道1是3F800000,100是42C80000,但我不知道如何返回任何数字的这些结果。如果有人知道一个简单的公式或者甚至是一个复杂的方法来做这个,请分享。

1个回答

3

我不知道是否正确考虑到了边缘情况,但这里是一些代码:

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

上面的第一个代码片段缺少对非规格化数的支持。我也不确定它是否正确处理负数。 - Roland Illig
自己的笔记:我不应该使用 Math.pow(x, y) 函数,因为它只是“返回将 xy 次幂的结果近似实现依赖的值。” [ECMA-262,15.8.2.13] - Roland Illig

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接