Java数学函数的精度

Java数学函数的精度,java,Java,我编写了一些浮点密集型算法,但无法获得预期的结果(实际上我正在尝试将我的旧C程序迁移到Java中)。还是Java的新手,2个月大因此,我对数学函数进行了一些快速测试,发现: System.out.printf("Math.PI^1 = %.22f MAth.PI^10 = %.22f \n", Math.pow(Math.PI, 1.0), Math.pow(Math.PI, 10.0)); 输出如下: Math.PI^1 = 3.1415926535897930000000 MAt

我编写了一些浮点密集型算法,但无法获得预期的结果(实际上我正在尝试将我的旧C程序迁移到Java中)。还是Java的新手,2个月大因此,我对数学函数进行了一些快速测试,发现:

System.out.printf("Math.PI^1 = %.22f MAth.PI^10 = %.22f \n", 
    Math.pow(Math.PI, 1.0), Math.pow(Math.PI, 10.0));
输出如下:

Math.PI^1 = 3.1415926535897930000000 MAth.PI^10 = 93648.0474760829800000000000 
93,648.047476083020973716690184919 
printf("M_PI^1 = %.22f M_PI^10 = %.22f \n", pow(M_PI,1.0), pow(M_PI, 10.0));

$ ./a.exe
M_PI^1 = 3.1415926535897931159980 M_PI^10 = 93648.0474760829820297658443
作为参考,计算器给出pi^10,如下所示:

Math.PI^1 = 3.1415926535897930000000 MAth.PI^10 = 93648.0474760829800000000000 
93,648.047476083020973716690184919 
printf("M_PI^1 = %.22f M_PI^10 = %.22f \n", pow(M_PI,1.0), pow(M_PI, 10.0));

$ ./a.exe
M_PI^1 = 3.1415926535897931159980 M_PI^10 = 93648.0474760829820297658443
,这是我期望得到的。小错误是正常的,因为C数学函数(Cygwin环境)的输出如下所示:

Math.PI^1 = 3.1415926535897930000000 MAth.PI^10 = 93648.0474760829800000000000 
93,648.047476083020973716690184919 
printf("M_PI^1 = %.22f M_PI^10 = %.22f \n", pow(M_PI,1.0), pow(M_PI, 10.0));

$ ./a.exe
M_PI^1 = 3.1415926535897931159980 M_PI^10 = 93648.0474760829820297658443

我错过了一些重要的事情吗?或者我没有指定的任何Java配置?是的,我已经搜索并找到了关于StrictMath和strictfp的关键字。但它们在Java中仍然给出相同的结果。

是的。Java和C都不擅长浮点数。您应该真正使用浮点来进行估计,而不是精确的值。任何高于货币的东西都会有一些不一致


如果需要精度,则需要使用BigDecimal。您需要测试它的性能,但您应该得到您想要的答案。

float大约有7.2个有意义的小数位数。
double有大约15.9个有意义的十进制数字

您的示例具有相同的16个第一位有意义的十进制数字。这意味着两段代码都得到了完全相同的二进制结果。假设您的C编译器对64位浮点使用与java相同的IEEE标准,这是可能的


在这16位数字之后,您看到的差异不是来自数学运算的执行方式,也不是来自舍入误差,而是来自不同的打印函数处理从二进制双精度文本转换为十进制文本的方式

如果需要精度,请使用
BigDecimal
类<代码>浮点数和双点数容易出现舍入错误:

BigDecimal test = new BigDecimal(Math.PI);
System.out.println(test.pow(10));
输出:

93648.047476082984468098606014523496270846023460034084392213341627785026090824331731984972528115769903975226563675097646096540840763090025329795404848362261074645725271801449534201154156882626448229305882846309041714031912085482167747513529870413304782789850546982088436194217303991296601721838481502870491848946572115751298158673535944440534837506339231012720875785869801057751354819447705578284437850332884027121079143234804533501909778030213894750577452441575587727129459381103515625

您之所以看到这些舍入,是因为
double
type是根据java()实现的。反过来:

给出15–17位有效十进制数字的精度


因此,您看到的精度来自标准。正如在其他答案中建议的那样,可以考虑使用<代码> BigDecimal >代码>更高的精度。

< P>最快的方法是使用这个,给出了9364840776083097、16690181819345、63599、15727、14699412705244的值,如果需要的话,可以得到更多的数字。我们看到cygwin C代码只正确到15位

93648.0474760829820297658443   C 
93648.04747608298              Java
93648.04747608302097371669018491934563599815727551469412705244
93648.047476082984468098606014523496270846023460034084392213341627
这两个系统的精度完全相同。您希望获得相同的精度,因为两者都可能使用双精度浮点。您可以说java answer更好,因为它不会因为显示更多的数字而产生错误的准确性

除非你对计算π的位数或其他与数论相关的任务特别感兴趣,否则16位数的精度将满足你的需要。我从未见过BigDecimal被证明是有用的应用程序,它需要大量的工作才能正确使用

一个大的十进制解是

    MathContext mc = MathContext.DECIMAL128;
    BigDecimal pi = new BigDecimal("3.141592653589793238462643383279503",mc);
    BigDecimal res = pi.pow(10, mc);
    out.println(pi);
    out.println(res);
它使用一个特定的MathContext,这是最精确的预定义上下文。如果这些数字是近似值,就像pi一样,那么最好指定一个MathContext。唯一一次你真的想在没有MathContext的情况下使用BigDecimal是如果你的值是精确的,我没有遇到过你想使用这个的时候

我们使用字符串构造函数和从Wolfram alpha和MathContext获得的值来修复精度。在计算幂时,我们也使用相同的MathContext。其结果是

3.141592653589793238462643383279503
93648.04747608302097371669018491938

如果我们将其与934结束的实际结果进行比较,我们会发现结果的最后一位有错误。一般来说,您希望大多数数学算法的正确性在最后一位的一个单位内,pow稍差一些,有2 ulp错误。使用MathContext意味着我们不会显示虚假的错误数字。

如果您想在
Java
中获得精度,请使用
BigDecimal
float
double
容易出现舍入错误。如果您想获得合理的性能,请不要使用
BigDecimal
。正如Jose Antonio Dura Olmos在中指出的,两个结果都是相同的,只是打印方式不同。@brso05这不是Java特有的:根据IEEE,浮点和双作用,具有可选的
strictfp
,并且数学函数的精度通常以ULP的形式记录。@Marco13我从来没有说过它是Java特有的。。。“这很可能是一个不正确的值。”你为什么不在评论之前先弄清楚它。让OP决定你不需要为他做决定。不管是谁否决了正确的答案,至少应该说明原因。@Salixalba阅读OP的问题
“小错误是可以的”
在执行
BigDecimal.pow()时,精度会起作用
输入足够接近,当
BigDecimal.pow()时
被调用结果非常接近它应该达到的程度。@Salixalba PI的精度不是问题,如果PI是
双精度
就可以了,但当你做PI^10时,如果你先将PI转换成
大十进制
,然后再做
pow(10),则会出现精度问题
OP想要什么应该没问题,记住完全阅读问题“小错误没问题“.你真的应该让OP决定什么是对的。不要无缘无故地投反对票,还有谁投了正确答案的反对票。你不应该否决正确的答案!是的,我在C n Java中比较了这些结果。BigDecimal真是太棒了!但正如u所说,它的实际用途可能是一个问题。我的代码algo需要很多三角公式。比如说,小数点后第五位的错误将导致最终结果的更大错误。小数点后13位或以上的错误可以。然而,我仍然依赖于那种“双重”数据类型。为什么不呢?它应该是IEEE754 64位兼容的。我尝试的另一种方法是重新调整代码中的公式,例如将长表达式分解成块。我遇到过职业选手