首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java BigDecimal -需要解释

Java BigDecimal -需要解释
EN

Stack Overflow用户
提问于 2016-02-17 09:38:29
回答 3查看 396关注 0票数 3

我使用了BigDecimal,但是对于两个不同的(数学上相同的)表达式,我仍然得到了不同的结果:

第一个表达式: PI - (10^(-14)/PI)

第二个表达式:(PI^2-10^(-14))/PI

简单地说,这里有一个等式:

代码语言:javascript
复制
package set1;

import java.math.BigDecimal;
import java.math.RoundingMode;

public class FloatingLaws {
    final static BigDecimal PI = BigDecimal.valueOf(Math.PI);
    public static void main(String[] args) {        
        System.out.println(firstExpression());
        System.out.println(secondExpression());

    }

    private static BigDecimal secondExpression() {
        return PI.subtract((BigDecimal.valueOf(Math.pow(10, -14)).divide(PI,50,RoundingMode.CEILING)));

    }

    private static BigDecimal firstExpression() {
        return (PI.multiply(PI).subtract(BigDecimal.valueOf(Math.pow(10, -14)))).divide(PI, 50,RoundingMode.CEILING);
    }

}

执行此代码后,不管舍入有多大,最后一个数字总是不同的。在我的例子中,我得到了以下两个结果:

代码语言:javascript
复制
3.14159265358978981690113816209304300915191180404867
3.14159265358978981690113816209304300915191180404866

我的问题是,为什么会发生这种情况,它是否可以解决?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2016-02-17 09:56:54

这是因为你要这样做:

pi - ((10^-4)/pi)<-只有括号中的部分是上限的,

这与

((pi^2-10^-14)/pi)<- -其中整个表达式都是上限的。

您使用BigDecimal,并且有精度为50的四舍五入模式上限。在这两个表达式中,当您除以PI数时,将应用上限。所以,如果你像第一个表达式那样被PI除以,那么你可能得到不太准确的结果--因为你在你的公式完全执行之前,CEIL中间值,所以你松开了PI运算中的上限部分,这在进一步的计算中产生了“错误”效应。当你最后除以PI时,就像在第二个表达式中一样,你使用了更精确的公式,它只使用结果上限,而不是像第一个表达式中的中间值,所以它计算更精确,只舍入结果,而不是中间值。

票数 5
EN

Stack Overflow用户

发布于 2016-02-17 10:08:51

BigDecimal.subtract方法总是在两个BigDecimal数之间产生精确的差,没有舍入。另一方面,BigDecimal.divide通常对结果进行舍入。在您的情况下,您使用的是CEILING舍入模式,该模式向上(朝向+无穷大)。在计算a-ceil(b/a)时,本质上是舍入整个结果(假设a已经四舍五入),而计算ceil((a*a-b)/a)则是四舍五入。这就是为什么firstExpression()更大的原因。如果您使用HALF_EVEN舍入,结果将是相同的。如果您使用FLOOR模式,结果将是相反的。

还请看一下什么是BigDecimal.valueOf(Math.PI);

代码语言:javascript
复制
System.out.println(BigDecimal.valueOf(Math.PI));
> 3.141592653589793

它甚至不接近实际的PI数(考虑到您需要50位数字)。您应该像这样显式地定义PI:

代码语言:javascript
复制
final static BigDecimal PI = new BigDecimal("3.14159265358979323846264338327950288419716939937510");

现在的结果如下:

代码语言:javascript
复制
3.14159265358979005536378154537278750652190194908786
3.14159265358979005536378154537278750652190194908785

这和你的完全不同。

票数 3
EN

Stack Overflow用户

发布于 2016-02-17 10:20:07

我修改了您的程序,以尝试java所知道的所有舍入模式。在oracle 8.72下运行,对于舍入模式HALF_UP、HALF_DOWN和HALF_EVEN,我得到了相同的结果。但是Krzysztof是对的,因为你不在相同的地方四舍五入,错误必然会出现。

代码语言:javascript
复制
public class FloatingLaws {
    final static BigDecimal PI = BigDecimal.valueOf(Math.PI);

    public static void main(String[] args) {
        for (RoundingMode roundingMode : RoundingMode.values()) {
            System.out.println(roundingMode);
            System.out.println("Equal? "+firstExpression(roundingMode).equals(secondExpression(roundingMode)));
            System.out.println(firstExpression(roundingMode));
            System.out.println(secondExpression(roundingMode));
        }

    }

    private static BigDecimal secondExpression(RoundingMode roundingMode) {
    return PI.subtract((BigDecimal.valueOf(Math.pow(10, -14)).divide(PI, 50, roundingMode)));

    }

    private static BigDecimal firstExpression(RoundingMode roundingMode) {
    return (PI.multiply(PI).subtract(BigDecimal.valueOf(Math.pow(10, -14)))).divide(PI, 50, roundingMode);
    }

}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/35452854

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档