繁体   English   中英

Java BigDecimal - 需要解释

[英]Java BigDecimal - explanation needed

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

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

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

更简单地说,这是等式: 带有PI的等式

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);
    }

}

执行此代码后,无论舍入有多大,最后一位数总是不同的。 在我的情况下,我得到这两个结果:

3.14159265358978981690113816209304300915191180404867
3.14159265358978981690113816209304300915191180404866

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

这是因为你这样做:

pi - ((10^-4)/pi) < - 只有括号中的部分被剔除,

这与...不同

((pi^2-10^-14)/pi) < - 其中整个表达被剔除。

您使用BigDecimal并且您具有精确度为50的舍入模式CEILING。在两个表达式中,当您除以PI编号时应用上限。 因此,如果您像第一个表达式中那样切换PI,那么您可能会得到不太准确的结果 - 因为您的CEIL中间值,在您的公式完全执行之前,所以您通过PI操作将CEILED部分从中断,这在进一步的计算中创建“错误”效应。 当你最后除以PI时,就像在第二个表达式中一样,你使用更准确的公式,它只是结果的上限,而不是像第一个表达式中的中间值,所以它计算得更精确,只舍入结果而不是中间值。

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

另请BigDecimal.valueOf(Math.PI);

System.out.println(BigDecimal.valueOf(Math.PI));
> 3.141592653589793

它甚至不接近实际的PI编号(考虑到你需要50位数)。 您应该明确定义PI,如下所示:

final static BigDecimal PI = new BigDecimal("3.14159265358979323846264338327950288419716939937510");

现在的结果如下:

3.14159265358979005536378154537278750652190194908786
3.14159265358979005536378154537278750652190194908785

这与你的完全不同。

我修改了你的程序来尝试java知道的所有舍入模式。 在oracle jdk 8.72下运行,我得到了舍入模式HALF_UP,HALF_DOWN和HALF_EVEN的相同结果。 但Krzysztof是对的,因为你不是在同一个地方四舍五入,错误肯定会弹出。

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);
    }

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM