簡體   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