Java Math.pow导致结果错误

13

如果您尝试运行以下代码:

public class Main {
   public static void main(String[] args) {
       long a = (long)Math.pow(13, 15);
       System.out.println(a + " " + a%13);
   }
}

你将得到"51185893014090752 8"

13^15的正确值为51185893014090757,即比Math.pow返回的结果大5。有什么想法是什么原因导致这种情况吗?


6
Math.pow返回浮点数,无论强制转换为何种数据类型,都不能期望其完全准确。即使强制转换是“有效的”,也是如此。 - Mat
但在你为精度的缺失感到惊慌之前,要知道并不是很多应用需要达到0.00000000001的精度,因此完美的精度也没有实际意义。唯一我能想到的常见情况可能就是操纵概率时了。 - Tony Ennis
答案中已经提到了原因。如果你需要整数幂,请查看java.math.BigInteger或commons-math org.apache.commons.math.util.MathUtils - Christian Semrau
Math.pow的结果可能会令人惊讶:https://octoperf.com/blog/2018/03/16/java-math-pow/ - Jerome L
6个回答

11

您已超出双精度浮点值可用的有效数字数量(约为15到16位)。一旦这样做,您就不能期望结果的最低有效位实际上是有意义/精确的。

如果您需要在Java中进行任意精度算术运算,请考虑使用BigIntegerBigDecimal


6
问题在于,随着你逐渐增加 double 值的大小,连续值之间的间隔会变大——double 不能表示其范围内的每个整数值,这就是出错的原因。它返回的是最接近精确结果的 double 值。

2
我想你的意思是粒度(粗细程度)增加了? - Peter Lawrey
他使用了正确的词语 - 它意味着一组数据中的细节级别。 - Paul Bellora
@PeterLawrey,不管对错如何,显然它并不像可能的那样清晰。编辑后希望能更加明确。 - Jon Skeet

6
这不是精度问题。Math.pow方法执行结果的近似计算。要获取正确的结果,请使用以下代码。
long b = 13;
for(int i = 0; i != 14; i ++) {
    b = b * 13;
}
System.out.println(b);

输出结果应为预期结果51185893014090757L。

更一般地说,当指数为整数时,应避免使用Math.pow方法。首先,结果是近似值,其次计算成本更高。

Math.pow的实现(以及Math类中的大多数其他方法)都基于网络库netlib,作为“自由可分发数学库”包(请参见StrictMath javadoc)。 C语言的实现可在e_pow.c中找到。


1

双精度浮点数具有有限的精度,其尾数为52位,大约相当于15到16个小数位。因此,您正在尝试计算的数字无法再由双精度浮点数(精确地)表示。


0

正确答案是提供最接近可以由 double 表示的数字。

你检查过这个情况了吗?


0

这是因为将 long 强制转换为 double、float 时,数字的保留位数有限,可能会出现一些错误。你应该自己处理计算中的数字,将它们保存在一个数组中,这并不是一种简单的方法。

但在 Python 编程语言中,你可以得到任意长度的结果,它非常强大!

祝你成功!


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