繁体   English   中英

Node.js 最大安全浮点数

[英]Node.js Maximum Safe Floating-point Number

在 Node.js 中,是否有像Number.MAX_SAFE_INTEGER这样的最大安全浮点数?

我做了一个小实验来找出我可以用来减去 0.13 的(近似)数字:

console.log(Math.floor(Number.MAX_SAFE_INTEGER));  // 9007199254740991
console.log(Math.floor(Number.MAX_SAFE_INTEGER)-0.13);  // 9007199254740991

console.log(Math.floor(Number.MAX_SAFE_INTEGER/2));  // 4503599627370495
console.log(Math.floor(Number.MAX_SAFE_INTEGER/2)-0.13);  // 4503599627370495

console.log(Math.floor(Number.MAX_SAFE_INTEGER/4));  // 2251799813685247
console.log(Math.floor(Number.MAX_SAFE_INTEGER/4)-0.13);  // 2251799813685246.8

console.log(Math.floor(Number.MAX_SAFE_INTEGER/64));  // 140737488355327
console.log(Math.floor(Number.MAX_SAFE_INTEGER/64)-0.13);  // 140737488355326.88

console.log(Math.floor(Number.MAX_SAFE_INTEGER/128));  // 70368744177663
console.log(Math.floor(Number.MAX_SAFE_INTEGER/128)-0.13);  // 70368744177662.87

我的猜测是,随着目标精度的增加,最大值会减小。

精度为 1 个十进制数字,您可以使用的最大数字为562949953421311

精确到 2 个十进制数字,它是70368744177663 有趣的是,第一个数字等于:

(Number.MAX_SAFE_INTEGER + 1) / 16 - 1

第二个数字等于:

(Number.MAX_SAFE_INTEGER + 1) / 128 - 1

我们正在寻找的是支持小数点后d位精度的最大安全数字。 “支持”是指“可以可靠地进行基本算术”。

例如,我们知道, Number.MAX_SAFE_INTEGER (aka 2**53-1)并不安全,因为基本的算术被打破:

Number.MAX_SAFE_INTEGER - 0.1 === Number.MAX_SAFE_INTEGER
>>> true // unsafe

我们知道 0安全的,因为:

0 + 0.1 === 0
>>> false // safe

顺便说一句,就1e-323 (包括)而言, 0是可靠的:

0 + 1e-323 === 0
>>> false // safe

0 + 1e-324 === 0
>>> true // unsafe

我在 0 和Number.MAX_SAFE_INTEGER之间进行二分搜索,寻找能够满足该定义的最大数字,并Number.MAX_SAFE_INTEGER这些数字。

这是代码(将任何其他数字传递给代码段末尾的findMaxSafeFloat()

 /**Returns whether basic arithmetic breaks between n and n+1, to a precision of `digits` after the decimal point*/ function isUnsafe(n, digits) { // digits = 1 loops 10 times with 0.1 increases. // digits = 2 means 100 steps of 0.01, and so on. let prev = n; for (let i = 10 ** -digits; i < 1; i += 10 ** -digits) { if (n + i === prev) { // eg 10.2 === 10.1 return true; } prev = n + i; } return false; } /**Binary search between 0 and Number.MAX_SAFE_INTEGER (2**53 - 1) for the biggest number that is safe to the `digits` level of precision. * digits=9 took ~30s, I wouldn't pass anything bigger.*/ function findMaxSafeFloat(digits, log = false) { let n = Number.MAX_SAFE_INTEGER; let lastSafe = 0; let lastUnsafe = undefined; while (true) { if (log) { console.table({ '': { n, 'Relative to Number.MAX_SAFE_INTEGER': `(MAX + 1) / ${(Number.MAX_SAFE_INTEGER + 1) / (n + 1)} - 1`, lastSafe, lastUnsafe, 'lastUnsafe - lastSafe': lastUnsafe - lastSafe } }); } if (isUnsafe(n, digits)) { lastUnsafe = n; } else { // safe if (lastSafe + 1 === n) { // Closed in as far as possible console.log(`\\n\\nMax safe number to a precision of ${digits} digits after the decimal point: ${n}\\t((MAX + 1) / ${(Number.MAX_SAFE_INTEGER + 1) / (n + 1)} - 1)\\n\\n`); return n; } else { lastSafe = n; } } n = Math.round((lastSafe + lastUnsafe) / 2); } } console.log(findMaxSafeFloat(1));

通过排列安全数字发现一件有趣的事情是指数不会以一致的方式增加。 看下表; 偶尔,指数增加(或减少)4,而不是3。不知道为什么。

| Precision | First UNsafe                | 2^53/x                   |
|-----------|-----------------------------|--------------------------|
| 1         | 5,629,499,534,21,312 = 2^49 | x = 16 = 2^4             |
| 2         | 703,687,441,77,664 = 2^46   | x = 128 = 2^7            |
| 3         | 87,960,930,22,208 = 2^43    | x = 1,024 = 2^10         |
| 4         | 5,497,558,13,888 = 2^39     | x = 16,384 = 2^14        |
| 5         | 68,719,476,736 = 2^36       | x = 131,072 = 2^17       |
| 6         | 8,589,934,592 = 2^33        | x = 1,048,576 = 2^20     |
| 7         | 536,870,912 = 2^29          | x = 16,777,216 = 2^24    |
| 8         | 67,108,864 = 2^26           | x = 134,217,728 = 2^27   |
| 9         | 8,388,608 = 2^23            | x = 1,073,741,824 = 2^30 |

更新:我对这个问题的理解是:是否有最大浮点数,介于0和那个之间,所有浮点数操作都可以安全传递。

如果这是问题,简短的回答是:

实际上,所有编程语言中都没有 MAX_SAFE_FLOAT(如果有的话会很高兴)。 编程语言中的数字以01位存储。 只要存储有限制(32 位、64 位等),可以表示的数字就是有限的。 但是,浮点数的数量是无限的。

考虑00.000000001之间的浮点数,需要表示多少个数字? 无限。 让计算机准确地存储无限的可能性是不可能的。 这就是为什么永远不会有 MAX_SAFE_FLOAT 的原因。

ps 在 JavaScript 中,所有数字都是 64 位双精度浮点数。 JavaScript 中没有浮点数和整数。

指数以一致的方式减少,因为它取决于小数部分的大小(二进制)。

  • 对于精度1 ,最大小数部分大小为9 -> 1001 -> size = 4
  • 对于精度2 ,最大小数部分大小为99 -> 1100011 -> size = 7
  • 对于精度3 ,最大小数部分大小为999 -> 1111100111 -> size = 10
  • ...

如您所见,我们的数字与@giladbarnea 发现的数字相同。

基于这一观察,我们可以轻松地编写一个最简单的解决方案来找到可以与任何整数相关的所有精度。

const precisions = [...new Array(16).keys()]
  .reverse()
  .map(value => {
    const int = 53 - (value && BigInt('9'.repeat(value)).toString(2).length)

    return int > 0
      ? 2 ** int - 1
      : 0
  })

function getSafePrecision (value) {
  const abs = Math.abs(value)

  return 15 - precisions.findIndex(precision => precision >= abs)
}

示例:

getSafePrecision(2 ** 43 - 1) // 3
getSafePrecision(2 ** 43) // 2

您正在寻找Number.MAX_VALUENumber.MIN_VALUE

Number.MAX_VALUE1.7976931348623157e+308Number.MIN_VALUE5e-324

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM