Java BigDecimal - 需要解释

mrG*_*own 3 java floating-point floating-accuracy bigdecimal

我正在使用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);
    }

}
Run Code Online (Sandbox Code Playgroud)

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

3.14159265358978981690113816209304300915191180404867
3.14159265358978981690113816209304300915191180404866
Run Code Online (Sandbox Code Playgroud)

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

Krz*_*cki 5

这是因为你这样做:

pi - ((10^-4)/pi)< - 只有托架中的部分是天花板的,

这与...不同

((pi^2-10^-14)/pi)< - 整个表达被玷污的地方.

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