【问题标题】:Comparing big numbers in Javascript比较 Javascript 中的大数
【发布时间】:2017-11-04 06:31:59
【问题描述】:

我有两个数字要比较。以下示例中的数字是 26^26 在两个不同系统中计算的结果。其中之一是我的 javascript 代码。

但是,当比较这两个数字时,我会得到这样的结果:

AssertionError [ERR_ASSERTION]: 4.0329146112660565e+26 == 4.0329146112661e+26

它们显然不相等,但理论上应该。

在 javascript 中对大数执行相等的正确方法是什么(即使它是一个近似值)?

【问题讨论】:

  • 您可以将数字转换为字符串然后进行比较
  • 你说的平等是什么意思?严格相等?或在某些数字内相等?或在固定精度内相等?或可以相互四舍五入的数字?请说明
  • 请记住:在 javascript 中,每个 Number 都是一个 64 位浮点数,因此 Number.MAX_SAFE_INTEGER 常量表示 JavaScript 中的最大安全整数 (2**53 - 1)。 编辑:因此,如果您以相同的方式计算和舍入它们,则只能检查两个浮点数的“相等”。当您拥有真正的 int 时,您将拥有咒语:只存储 x 数量的 LSB,而使用浮点数时,您只需要存储前 53 个 MSB 的咒语(其余的被丢弃)。
  • @GitaarLAB 很好的答案!

标签: javascript math int comparison-operators


【解决方案1】:

如果您要做的是确定两个数字实际上是否相等,您将不得不提出您的误差范围。一种方法是计算数字之间的差异,然后确定差异是否显着。

因此,从您之前的数字中提取,我们可以通过减法来评估这些数字之间的差异。由于我们并不真正关心这种差异的符号,因此我将继续获取差异的绝对值。

Math.abs(4.0329146112660565e+26 - 4.0329146112661e+26) === 4329327034368

(旁注:现在不是解释原因的时候,但是 JavaScript 中的 == 运算符具有令人困惑和容易出错的行为,当你想比较值时使用 ===。)

这种差异是一个巨大的数字,但首先与我们的数字有多大有关,它相当微不足道。直观地说,我很想将差值除以我们原始数字中的最小值,如下所示:

4329327034368 / 4.0329146112660565e+26 === 1.0734983136696987e-14

这看起来是一个很小的数字。对一堆值重复相同的操作,您应该能够确定您想要的误差范围。然后,您所要做的就是对任意数字执行相同的操作,看看这个“差异比”是否对您来说足够小。

function similar(a, b) {
  let diff = Math.abs(a - b);
  let smallest = Math.min(Math.abs(a), Math.abs(b));
  let ratio = diff / smallest;
  return ratio < MARGIN_OF_ERROR;
}

现在我刚刚想出了确定两个数字之间差异重要性的方法。它可能不是一种非常聪明的计算方式,它可能适用于某些情况而不适用于其他情况。但总体思路是,您必须创建一个函数来确定两个值是否足够接近您自己的“关闭”定义。

请注意,JavaScript 是您可以用来进行数学运算的最糟糕的语言之一。当整数超出 Number.MAX_SAFE_INT 时(根据 Chrome 似乎是 9007199254740991,不确定它是否因浏览器而异,或者如果这是一个标准化的常数)。

【讨论】:

  • 标准化常数! IEEE 754 float 的自然结果:记住咒语,只存储 53 FIRST 位(和一个指数)编辑:我们不包括最后一个可能值(但减去一个)的原因是因为我们想要 SAFE int,我们无法区分 253+1 和 253
  • 这实际上适用于相对精度,而不是固定,但是是的,它绝对可以帮助
  • 旁注:javascript数学没有错(我经常这样做)。只要明白只有一种数字类型:IEEE 747 64bit float。你仍然有我的+1!编辑:您甚至可以使用大于该数字的数字进行可靠的数学运算,只要您要表示的值不需要超过 53 个有效位!一旦了解您可以使用本机数字实现非常有趣的算法(通常是二进制)
【解决方案2】:

更新:如果您的目标引擎是es2020 或更高版本,您可以使用新的BigInt javascript 原语,用于高于Number.MAX_SAFE_INTEGER 的数字

BigInt(4.0329146112660565e+26) === BigInt(4.0329146112661e+26)
//false

See more information in MDN

【讨论】:

    【解决方案3】:
    var a = 4.0329146112660565e+26;
    
    var b = 4.0329146112661e+26;
    
    a = Math.round(a/10e+20)*10e+20
    
    b = Math.round(b/10e+20)*10e+20
    
    a == b;
    

    【讨论】:

    • @Daniel Lord 谢谢! 10e+20 似乎有点神奇。你介意解释一下它背后的逻辑吗?这个解决方案结合@GitaarLAB 关于Number.MAX_SAFE_INTEGER 的建议对我有用。 Math.round(4.0329146112661e+26 / Number.MAX_SAFE_INTEGER) * Number.MAX_SAFE_INTEGER;
    • @Sam D. 似乎您希望精度小于 12,因为这是浮点数不同的地方。此解决方案的精度为 26 - 20 + 1 = 7。浮点数的精度为 7 逻辑。更改 20 以提高或降低比较的精度。
    • 为什么要将 2026 转换为数字,它已经是一个数字:10e+20 == 10e20
    【解决方案4】:

    我建议使用大数字库之一:

    1. big.js (https://www.npmjs.com/package/big.js)

    例子:

    var x = new Big('4.0329146112660565e+26');
    
    var y = new Big('4.0329146112661e+26');
    
    // Should print false
    console.log('Comparision result' + x.eq(y));
    
    1. 大数字 (https://www.npmjs.com/package/big-numbers)

    例子:

    var x = bn.of('4.0329146112660565e+26');
    var y = bn.of('4.0329146112661e+26');
    
    // Should print false
    console.log('Comparision result' + x.equals(y));
    

    【讨论】:

      猜你喜欢
      • 2012-04-16
      • 1970-01-01
      • 1970-01-01
      • 2011-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多