【问题标题】:How to convert a floating point number to its binary representation (IEEE 754) in Javascript?如何在Javascript中将浮点数转换为其二进制表示(IEEE 754)?
【发布时间】:2011-03-07 00:21:42
【问题描述】:

在 Javascript 中将浮点数转换为其二进制表示的最简单方法是什么? (例如 1.0 -> 0x3F800000)。

我曾尝试手动执行此操作,这在某种程度上有效(使用通常的数字),但对于非常大或非常小的数字(无范围检查)和特殊情况(NaN、无穷大等)它会失败:

function floatToNumber(flt)
{
    var sign = (flt < 0) ? 1 : 0;
    flt = Math.abs(flt);
    var exponent = Math.floor(Math.log(flt) / Math.LN2);
    var mantissa = flt / Math.pow(2, exponent);

    return (sign << 31) | ((exponent + 127) << 23) | ((mantissa * Math.pow(2, 23)) & 0x7FFFFF);
}

我是在重新发明轮子吗?

编辑:我改进了我的版本,现在它可以处理特殊情况。

function assembleFloat(sign, exponent, mantissa)
{
    return (sign << 31) | (exponent << 23) | (mantissa);
}

function floatToNumber(flt)
{
    if (isNaN(flt)) // Special case: NaN
        return assembleFloat(0, 0xFF, 0x1337); // Mantissa is nonzero for NaN

    var sign = (flt < 0) ? 1 : 0;
    flt = Math.abs(flt);
    if (flt == 0.0) // Special case: +-0
        return assembleFloat(sign, 0, 0);

    var exponent = Math.floor(Math.log(flt) / Math.LN2);
    if (exponent > 127 || exponent < -126) // Special case: +-Infinity (and huge numbers)
        return assembleFloat(sign, 0xFF, 0); // Mantissa is zero for +-Infinity

    var mantissa = flt / Math.pow(2, exponent);
    return assembleFloat(sign, exponent + 127, (mantissa * Math.pow(2, 23)) & 0x7FFFFF);
}

我仍然不确定这是否 100% 正确,但它似乎足够好。 (我仍在寻找现有的实现)。

【问题讨论】:

  • 我很想知道您为什么要转换为 IEEE 单精度表示。 Javascript 数字不是通常存储为双精度(64 位)数量吗?
  • 您正在为小值返回无穷大 (exponent &lt; -126);不知何故,我不认为那是你想要的。 (此外,-0.0 会以错误的符号位结束,但这可能对您的应用程序无关紧要。)
  • Mark Dickinson:我正在转换为 32 位 IEEE,因为应用程序将生成一些将在内存编辑器中使用的值(适用于地址 + 字节格式)。根据维基百科,“指数
  • 关于指数:输入可以是任何有效的 IEEE 754 双精度值;使用您当前的代码,如果您的输入像 1e-60 这样很小,您最终会输出无穷大的二进制表示。改为输出 0.0 的表示可能更合适,这是通过将双精度值四舍五入为单精度自然得到的。为了区分 0.0 和 -0.0,我知道的唯一方法是查看 atan2(flt, -1.0) 的结果(如果 JavaScript 提供了 copysign,那将是更好的方法)。
  • 你可以用类型化数组来做到这一点:stackoverflow.com/a/10564792/309483

标签: javascript binary floating-point ieee-754


【解决方案1】:

新技术使这变得简单,并且可能也更加向前兼容。我喜欢扩展内置原型,不是每个人都喜欢。因此,请随意将以下代码修改为经典程序方法:

(function() {
    function NumberToArrayBuffer() {
        // Create 1 entry long Float64 array
        return [new Float64Array([this]).buffer];
    }
    function NumberFromArrayBuffer(buffer) {
        // Off course, the buffer must be at least 8 bytes long, otherwise this is a parse error
        return new Float64Array(buffer, 0, 1)[0];
    }
    if(Number.prototype.toArrayBuffer)  {
        console.warn("Overriding existing Number.prototype.toArrayBuffer - this can mean framework conflict, new WEB API conflict or double inclusion.");
    }
    Number.prototype.toArrayBuffer = NumberToArrayBuffer;
    Number.prototype.fromArrayBuffer = NumberFromArrayBuffer;
    // Hide this methods from for-in loops
    Object.defineProperty(Number.prototype, "toArrayBuffer", {enumerable: false});
    Object.defineProperty(Number.prototype, "fromArrayBuffer", {enumerable: false});
})();

测试:

(function() {
    function NumberToArrayBuffer() {
        // Create 1 entry long Float64 array
        return new Float64Array([this.valueOf()]).buffer;
    }
    function NumberFromArrayBuffer(buffer) {
        // Off course, the buffer must be ar least 8 bytes long, otherwise this is a parse error
        return new Float64Array(buffer, 0, 1)[0];
    }
    if(Number.prototype.toArrayBuffer)  {
        console.warn("Overriding existing Number.prototype.toArrayBuffer - this can mean framework conflict, new WEB API conflict or double inclusion.");
    }
    Number.prototype.toArrayBuffer = NumberToArrayBuffer;
    Number.fromArrayBuffer = NumberFromArrayBuffer;
    // Hide this methods from for-in loops
    Object.defineProperty(Number.prototype, "toArrayBuffer", {enumerable: false});
    Object.defineProperty(Number, "fromArrayBuffer", {enumerable: false});
})();
var test_numbers = [0.00000001, 666666666666, NaN, Infinity, -Infinity,0,-0];
console.log("Conversion symethry test: ");
test_numbers.forEach(
      function(num) {
               console.log("         ", Number.fromArrayBuffer((num).toArrayBuffer()));
      }
);

console.log("Individual bytes of a Number: ",new Uint8Array((666).toArrayBuffer(),0,8));
&lt;script src="https://getfirebug.com/firebug-lite-debug.js"&gt;&lt;/script&gt;

【讨论】:

  • 所以基本上是new Uint8Array(new Float64Array([num]).buffer,0,8)。但是警告 """The bit pattern that might be observed in an ArrayBuffer after a Number value has been stored into it is not necessarily the same as the internal representation of that Number value used by the ECMAScript implementation.""" 是否仅指 irrelevant 位模式?或者它是否也允许最终影响 observable 值的位模式更改?
  • @Pacerier 这是一个非常好的问题,我将不得不做一些研究,也许会提出一个独立于平台的更好的解决方案。
  • @Pacerier 一些引擎将数据打包到不相关的位中,所以我认为警告只是为了解释这些。如果写入Float64Array 的位模式代表明显不同的值,我认为这将是一个错误。
  • 我可能是错的,但我对规范的解读是这是明确定义的。查看 ArrayBuffer 部分中的 NumericToRawBytes 和 RawBytesToNumeric 算法,它们在读取和存储 Float64 时明确指定 IEEE-754 格式。我相信关于“实现使用的内部表示......”的警告仅仅意味着没有指定 CPU 的内部浮点寄存器,但在 ArrayBuffers 中存储位时它必须转换为 IEEE754。
【解决方案2】:

这是一个适用于我测试过的所有东西的函数,除了它不区分 -0.0 和 +0.0。

它基于来自http://jsfromhell.com/classes/binary-parser 的代码,但它专门用于 32 位浮点数并返回整数而不是字符串。我还对其进行了修改以使其更快并且(稍微)更具可读性。

// Based on code from Jonas Raoni Soares Silva
// http://jsfromhell.com/classes/binary-parser
function encodeFloat(number) {
    var n = +number,
        status = (n !== n) || n == -Infinity || n == +Infinity ? n : 0,
        exp = 0,
        len = 281, // 2 * 127 + 1 + 23 + 3,
        bin = new Array(len),
        signal = (n = status !== 0 ? 0 : n) < 0,
        n = Math.abs(n),
        intPart = Math.floor(n),
        floatPart = n - intPart,
        i, lastBit, rounded, j, exponent;

    if (status !== 0) {
        if (n !== n) {
            return 0x7fc00000;
        }
        if (n === Infinity) {
            return 0x7f800000;
        }
        if (n === -Infinity) {
            return 0xff800000
        }
    }

    i = len;
    while (i) {
        bin[--i] = 0;
    }

    i = 129;
    while (intPart && i) {
        bin[--i] = intPart % 2;
        intPart = Math.floor(intPart / 2);
    }

    i = 128;
    while (floatPart > 0 && i) {
        (bin[++i] = ((floatPart *= 2) >= 1) - 0) && --floatPart;
    }

    i = -1;
    while (++i < len && !bin[i]);

    if (bin[(lastBit = 22 + (i = (exp = 128 - i) >= -126 && exp <= 127 ? i + 1 : 128 - (exp = -127))) + 1]) {
        if (!(rounded = bin[lastBit])) {
            j = lastBit + 2;
            while (!rounded && j < len) {
                rounded = bin[j++];
            }
        }

        j = lastBit + 1;
        while (rounded && --j >= 0) {
            (bin[j] = !bin[j] - 0) && (rounded = 0);
        }
    }
    i = i - 2 < 0 ? -1 : i - 3;
    while(++i < len && !bin[i]);
    (exp = 128 - i) >= -126 && exp <= 127 ? ++i : exp < -126 && (i = 255, exp = -127);
    (intPart || status !== 0) && (exp = 128, i = 129, status == -Infinity ? signal = 1 : (status !== status) && (bin[i] = 1));

    n = Math.abs(exp + 127);
    exponent = 0;
    j = 0;
    while (j < 8) {
        exponent += (n % 2) << j;
        n >>= 1;
        j++;
    }

    var mantissa = 0;
    n = i + 23;
    for (; i < n; i++) {
        mantissa = (mantissa << 1) + bin[i];
    }
    return ((signal ? 0x80000000 : 0) + (exponent << 23) + mantissa) | 0;
}

【讨论】:

  • 有没有一种简单的方法可以从二进制转换为浮点数,或者是问题还是反转这个算法?
  • 有什么逆向算法吗?
猜你喜欢
  • 2016-04-02
  • 1970-01-01
  • 1970-01-01
  • 2017-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多