【问题标题】:Node.js Maximum Safe Floating-point NumberNode.js 最大安全浮点数
【发布时间】:2017-08-29 01:40:12
【问题描述】:

在 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

我的猜测是随着目标精度的提高,最大值会降低。

【问题讨论】:

  • 您可以随时查阅规范;它可能比输入这个问题花费的时间更少。
  • 如果您能解释一下“安全”是什么意思,也会很有帮助。对于整数值,这是相当明显的,但对于实数则不太明显。

标签: javascript node.js floating-point


【解决方案1】:

精确到 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 之间进行二进制搜索,以找到符合该定义的最大数字,并得出这些数字。

这是代码(在 sn-p 末尾将任何其他数字传递给 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 |

【讨论】:

  • 哇!!这怎么只有3个赞成票。但要么我误解了,要么代码有问题..num = findMaxSafeFloat(1, true); num -= 0.1; num -=0.1; 给出了562949953421310.75。这可能是依赖于架构的吗?
  • 此外,指数跳跃并不奇怪,因为我们正在针对二进制系统测试十进制(以 10 为底)浮点数。您可以通过以不同精度为底数 2 的对数 (trunc(ln2(0.001)) - trunc(ln2(0.0001)) == 4) 来查看跳跃的大小
  • 5,629,499,534,21,312 是一个什么样的数字(具体是 ,21,312)
【解决方案2】:

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

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

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

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

附言在 JavaScript 中,所有数字都是 64 位双精度浮点数。没有浮点数v.s。 JavaScript 中的整数。

【讨论】:

  • 这是错误的。可表示的浮点数的数量绝对是不是“无限”的。只涉及 64 位;怎么可能是无限的?
  • @Pointy 我不是指可表示的浮点数,我指的是需要准确表示的浮点数。
  • 好吧,一方面,您的回答忽略了 JavaScript 具有 Number.MAX_VALUENumber.MIN_VALUE 的事实。绝对有一个可以存储的最大值;尾数的值只能和指数的值一样大。
  • ??在 0 和 0.00001 之间有无限的 实数 数,但在该范围内没有无限的 64 位 IEEE-754 二进制浮点数。这就是问题所在。 Number.MAX_VALUE 是“安全的”,因为它实际上是 IEEE-754 双精度格式可以表示的最大值。
  • @Pointy 我认为我们对这个问题有不同的理解。我的理解是:OP要求“安全范围”,在这个范围内所有浮点数都可以准确表示。
【解决方案3】:

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

  • 对于精度1,最大小数部分大小为9 -> 1001 -> 大小 = 4
  • 对于精度2,最大小数部分大小为99 -> 1100011 -> 大小 = 7
  • 对于精度3,最大小数部分大小为999 -> 1111100111 -> 大小 = 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

【讨论】:

    【解决方案4】:

    您正在寻找Number.MAX_VALUENumber.MIN_VALUE

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

    【讨论】:

    • 请注意,OP 要求最大的“安全”浮点数。我认为他/她想要的是安全范围,比如 Number.MAX_SAFE_INTEGER 和 Number.MIN_SAFE_INTEGER 之间的范围
    • @shaochuancs 然而,这并没有什么意义,因为实数的表示从不“安全”,因为整数表示可以被称为“安全”。
    • 是的,我知道要求“安全”浮点数没有多大意义。但是请看5“减0.13”操作,似乎OP试图找到最大的安全数,当“减0.13”时仍然给出正确的结果。他还提到“随着目标精度的增加,最大值减小”,似乎他猜测“减去0.1312134543124321512”时,最大的安全数会更小。
    猜你喜欢
    • 2017-07-24
    • 2011-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多