JavaScript和Java中的计算结果不同。

3

我想我在处理大数时遇到了问题。

Java中的计算:

int n = 4451 + 554 * 57;
n = n << 13 ^ n;
System.out.println(n * (n * n * 15731 + 789221) + 1376312589);
=> 587046333

在JavaScript中:
var n = 4451 + 554 * 57;
n = n << 13 ^ n;
console.log(n * (n * n * 15731 + 789221) + 1376312589);
=> 4.043454188561781e+29

JavaScript版本存在什么问题,我该如何修复,以使JavaScript的结果与Java的结果相同?
编辑:尝试使用:https://github.com/jtobey/javascript-bignum,但结果为0。
var test = new BigInteger(295120061).multiply( new BigInteger(295120061) 
                                      .multiply(new BigInteger(295120061)) 
                                      .multiply(new BigInteger(15731)) 
                                      .add(new BigInteger(789221)))
                                      .add(new BigInteger(1376312589));

=> 测试 = 0


如果在Java示例中将n声明为long,会发生什么? - ajb
然后我得到了-1448455493533196867。 - Captain Obvious
尝试使用“try parse long”和“parse int”而不是隐式变量。 - huseyin tugrul buyukisik
尝试使用 double(或 BigInteger,如果您需要精确结果) - fabian
3个回答

5

JavaScript中没有整数算术,所有数字都以64位浮点数(Java中的double)存储。当JavaScript看到像<<^这样的位处理运算符时,它会暂时将操作数转换为32位整数进行算术运算,但然后再将它们转换回64位浮点数。因此,在JavaScript中,最后一次乘法是以浮点运算执行的。而在Java中,则仍然是一个整数操作。下面是在Java中执行相同操作的代码(我已经测试过了,结果是相同的):

int n = 4451 + 554 * 57;
n = n << 13 ^ n;
double x = n;
System.out.println(x * (x * x * 15731 + 789221) + 1376312589);

如果你想要 JavaScript 代码与 Java 表现相同,你需要一种方式来执行乘法和加法,使其在溢出时与 Java 的处理方式相同。也就是说,它必须将所有操作的结果视为在范围 -231 和 231-1 内。使用 JavaScript 的本机算术运算并没有可靠的方法来做到这一点;即使你给它两个只有 31 位有效数字的值,当你将它们相乘时,你会得到 62 位有效数字,而 JavaScript 的“number”类型只有 52 位,这意味着某些位将会丢失。可能有一个 JavaScript 库可以让你进行这种精确的整数运算,但我不是 JavaScript 框架方面的专家,所以我不知道那是什么。也许其他人会提供帮助。

你说得对,现在它与JavaScript的结果相同,但是我需要反过来 :) (JavaScript的结果应该是587046333) - Captain Obvious
没问题,不管怎样还是谢谢您提供的其他信息;)我会尝试解决这个问题,因为这对我的应用程序非常重要:) - Captain Obvious
1
我已经尝试过了,但没有成功(我已经在问题中更新了它)。 - Captain Obvious
1
@CaptainObvious 如果您发布了如何使其工作的方法,它可以帮助其他正在尝试在JavaScript中执行准确整数运算的人。 - Jason C
1
可能有一个JavaScript库可以让你进行这种精确的整数运算。有的!请看下面我的答案! - Boann
显示剩余2条评论

3
问题,正如@ajb所述,是JavaScript是弱类型语言,并且正在执行双精度浮点运算,而我们需要严格的32位整数运算。
对于乘法,函数Math.imul存在于此目的。它尚未在Internet Explorer中得到支持,但链接页面包含一个替换函数,用于模拟旧版浏览器中的imul,该函数通过分别乘以数字的顶部和底部来工作。
对于加法,我们可以将数字与0进行OR运算。这有效,因为任何按位操作都会强制JavaScript数字变为32位整数,并且与0进行OR运算实际上不会改变该值:
Math.iadd = function(a, b) { return ((a|0) + (b|0))|0; }

现在使用它:

var n = Math.iadd(4451, Math.imul(554, 57));
n = n << 13 ^ n;
console.log(Math.iadd(Math.imul(n, Math.iadd(Math.imul(Math.imul(n, n), 15731), 789221)), 1376312589));

这段代码有点长,但是可以正常运行。输出结果为587046333,与Java相同。

谢谢这个,我已经点赞并会尽快查看。可能比额外的库更高效。 - Captain Obvious

1

使用:https://github.com/iriscouch/bigdecimal.js


(说明:此处为编程相关内容,引用了 bigdecimal.js 的 GitHub 链接)
var n = 4451 + 554 * 57; 
n=n << 13 ^ n; 
var test = new BigDecimal(n).multiply( new BigDecimal(n) 
                                      .multiply(new BigDecimal(n)) 
                                      .multiply(new BigDecimal(15731)) 
                                      .add(new BigDecimal(789221)))
                                      .add(new BigDecimal(1376312589));
    test.intValue()

打印正确的结果

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接