【问题标题】:JavaScript Integer math incorrect resultsJavaScript 整数数学不正确的结果
【发布时间】:2010-08-06 22:05:04
【问题描述】:

我只是想在 JS 中实现一个简单的 RNG。

发生的事情是 javascript 将 119106029 * 1103515245 评估为 131435318772912110 而不是 131435318772912105。我们知道这是错误的,因为两个奇数相乘不会得到偶数。

有人知道怎么回事吗?我只想要一个可靠的可重复 RNG,并且由于这些不正确的值,我无法获得与我的 C 实现相同的东西相匹配的结果。

【问题讨论】:

  • 如果我是你,我会问:为什么你首先需要乘以这么大的数字?这可能是一种在不使用此方法的情况下做你想做的事情的方法。除非它是一些科学问题领域,否则您可能会遇到设计错误。
  • 只做最简单的随机数生成器。乘以大数,加上一个常数,mod 乘以 2**32。我只对最后几位感兴趣!但他们错了。为什么?
  • Math.random() * 100000(00.....) 不够用的时候!?
  • 见杰森的回答。我需要一些确定性和可重现的东西。我想要一个特定的种子值来给我一个可以复制的特定随机序列。

标签: javascript integer overflow


【解决方案1】:

如果在 C/C++ (double) 中完成,最后的数字将是 ...112 而不是 105(这是正确的)。如果使用 'long double' 执行, 结果将如预期(...105)。所以看起来像 Javascript 解释器将数字转换为 8-byte-double 在内部,进行计算并进行一些未知的舍入 这导致比 C/C++ 标准稍微好一点的结果 双重计算。

GCC 4.5:

 int main(int argc, char** argv)
{
 long double a = 119106029;
 long double b = 1103515245;
 long double c = a * b;
 printf("%.Lf\n", c);

 return 0;
}

结果:

131435318772912105

预期:

131435318772912105

所以如果没有 借助 BIGNUM 库(如果有)。

问候

rbo

【讨论】:

    【解决方案2】:

    随着 BigInt 的到来,您现在可以准确地执行这些计算:

    console.log((119106029n * 1103515245n).toString());

    【讨论】:

      【解决方案3】:

      根据 ECMAScript 标准,JavaScript 中的所有数字默认都是(64 位 IEEE 754)浮点数。

      但是,所有 32 位整数都可以精确地表示为浮点数。您可以使用适当的按位运算符将结果强制为 32 位,如下所示:

      x = (a * b) >>> 0;  // force to unsigned int32
      x = (a * b) | 0;    // force to signed int32
      

      很奇怪,但这是标准。

      (顺便说一句,这种舍入行为是 one of the most frequently reported "bugs" 针对 Firefox 的 JavaScript 引擎。看起来今年到目前为止已经报告了 3 次......)

      如果您想要真正的整数数学,您可以使用 BigInt 值,这是一种不同类型的数字,末尾带有 n

      > 119106029n * 1103515245n
      131435318772912105n
      

      这是一个相对较新的 JS 功能,可能无法在旧浏览器中实现。


      至于 JavaScript 中可重现的随机数,V8 基准测试使用这个:

      // To make the benchmark results predictable, we replace Math.random
      // with a 100% deterministic alternative.
      Math.random = (function() {
        var seed = 49734321;
        return function() {
          // Robert Jenkins' 32 bit integer hash function.
          seed = ((seed + 0x7ed55d16) + (seed << 12))  & 0xffffffff;
          seed = ((seed ^ 0xc761c23c) ^ (seed >>> 19)) & 0xffffffff;
          seed = ((seed + 0x165667b1) + (seed << 5))   & 0xffffffff;
          seed = ((seed + 0xd3a2646c) ^ (seed << 9))   & 0xffffffff;
          seed = ((seed + 0xfd7046c5) + (seed << 3))   & 0xffffffff;
          seed = ((seed ^ 0xb55a4f09) ^ (seed >>> 16)) & 0xffffffff;
          return (seed & 0xfffffff) / 0x10000000;
        };
      })();
      

      【讨论】:

      • IIRC 我确实在我的应用程序中使用了 Robert Jenkins PRNG 的这个或其他一些变体 (htmltetris.com)
      【解决方案4】:

      当 javascript 中的整数太大而无法容纳 32 位值时,某些浏览器会将其转换为浮点数。由于浮点值仅保存到有限的精度,因此可能会在较大的值上进行一些舍入。

      【讨论】:

      • 结果对最后两位是正确的。如果它只是截断高位是有道理的,但无论如何我想要的只是结果的低 32 位。我怎么得到它?
      • @Steven:Javascript 并没有真正的整数类型,在这种情况下,您会看到浮点数的精度损失。并且那些保留数字的“上部”,而不是低位(就像整数乘法一样)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-24
      • 1970-01-01
      • 2019-09-15
      • 2012-01-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多