[英]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答案:浮動不再能夠准確地代表“正確”的答案。 請改用雙。 此外,乘法也將在沒有明確演員的情況下完成。
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.