簡體   English   中英

Java中的數學 - 不同對象的不同結果

[英]Math in Java - different results with different objects

我正在為我正在進行的應用程序計算得到一些奇怪的結果,我認為這里的某個人可能能夠幫助弄清楚發生了什么。

此特定計算的要求表明計算應如下所示:

A和B是已知的

A * B = C

對於這個特定的計算

A = 0.0410
B = 123456789010

以下是我看到的結果:

計算器:

0.0410 * 123456789010 = 5061728349.41

Java的:

B是雙:

0.0410f * 123456789010d = 5.061728489223363E9 = 5061728489.223363

B很長:

0.0410f * 123456789010l = 5.0617288E9 

對於我來說,精度的損失並不重要(我只需要9位數的精度),而不是10s和1s點的差異。 為什么使用double進行計算會給出“錯誤”結果?

順便說一句,我嘗試使用BigDecimal進行計算,並得到與使用double時相同的結果。

發生的各種類型轉換由JLS#5.6.2指定。 在你的情況下(摘錄):

  • 如果任一操作數的類型為double,則另一個操作數轉換為double。
  • 否則,如果任一操作數的類型為float,則另一個操作數轉換為float。

0.0410f * 123456789010d = 506172848.9223363 ,首先將0.0410f轉換為不必等於0.0410d 實際上你可以嘗試一下,看看那不是:

    double d1 = 0.041d;
    double d2 = 0.041f;
    System.out.println(new BigDecimal(d1));
    System.out.println(new BigDecimal(d2));

輸出:

0.041000000000000001720845688168992637656629085540771484375
0.041000001132488250732421875

在下一個示例中:

0.0410f * 123456789010L = 506172832

long被轉換為float,您可以使用此示例進行驗證:

    float f1 = 0.0410f;
    float f2 = 123456789010L;
    System.out.println(new BigDecimal(f1)); // 0.041000001132488250732421875
    System.out.println(new BigDecimal(f2)); // 123456790528
    System.out.println(new BigDecimal(0.0410f * 123456789010L)); // 5061728768
    System.out.println(new BigDecimal(f1 * f2)); // 5061728768

至於浮動/雙重操作的精度,請檢查這個問題

最后,如果你使用BigDecimal,你會得到正確的答案:

    BigDecimal a = new BigDecimal("0.041");
    BigDecimal b = new BigDecimal("123456789010");
    System.out.println(a.multiply(b)); // outputs 5061728349.410

TLDR答案:浮動不再能夠准確地代表“正確”的答案。 請改用雙。 此外,乘法也將在沒有明確演員的情況下完成。

答案我使用http://www.ideone.com

  A      B     C      
float  long  float  5061728768.000000
double long  double 5061728489.223363

問題是浮點數的精度遠小於一倍,所以當乘以一個大數(例如你的10 ^ 10值)時,你會在乘法中失去這個精度。 如果我們為乘法顯式地將A轉換為double:

double C = ((double)A)*B; //=5061728489.223363

然后我們回到額外的精度。 如果我們將雙回答轉回浮點數:

float C = (float)((double)((double)A)*B); //=5061728256.000000 

你看到答案又不同了。 使用乘法的結果類型,因此在這個實例中為double ,但是回退到float會降低精度。 如果沒有明確的大小寫( double C=A*B ),則使用float類型。 the multiplication. 對於兩個強制轉換,乘法都是雙精度,乘法精度會丟失。

第一個計算是使用double(64位),第二個浮點數(32位)。 你所看到的是“四舍五入的錯誤”。

在這兩種情況下,它都是浮點計算,但在第二種情況下,不涉及“雙”參數,因此它只使用32位算術。

引用Java語言規范

如果二元運算符的至少一個操作數是浮點類型,則操作是浮點運算,即使另一個是整數。

如果數值運算符的至少一個操作數是double類型,則使用64位浮點運算執行運算,並且數值運算符的結果是double類型的值。 如果另一個操作數不是double,則首先將其擴展(第5.1.5節)以通過數字提升(第5.6節)鍵入double。

否則,使用32位浮點運算執行運算,並且數值運算符的結果是float類型的值。 (如果另一個操作數不是浮點數,則首先將其擴展為通過數字提升鍵入float。)

您的問題的答案可能在Java語言規范的浮點操作部分這篇較舊的帖子中 由於隱含的轉換正在發生,您可能會遇到舍入錯誤。

適用於您的情況的報價是

第三次操作:

如果二元運算符的至少一個操作數是浮點類型,則操作是浮點運算,即使另一個是整數。

第二次操作:

如果數值運算符的至少一個操作數是double類型,則使用64位浮點運算執行運算,並且數值運算符的結果是double類型的值。 如果另一個操作數不是double,則首先將其擴展(第5.1.5節)以通過數字提升(第5.6節)鍵入double。

第一次操作

否則,使用32位浮點運算執行運算,並且數值運算符的結果是float類型的值。 (如果另一個操作數不是浮點數,則首先將其擴展為通過數字提升鍵入float。)

因此,您不應該擔心,但如果需要,可以決定您想要的精度是多少並使用適當的鑄件。

32位IEEE浮點數具有7位精度; 64位允許16.這就是你得到的。 如果這些都不夠,則必須使用BigDecimal。

在實現IEE標准的每種語言中都是如此,而不僅僅是Java。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM