Java中數值的加減乘除結果都是對的么?丨【奔跑吧!JAVA】
計算機程序內部都是二進制表示的,日常的編程實戰中,除了處理字符串外,大部分情況下,都在處理數值的計算,如計算多個值的和或平均值。在Java語言體系中,關于數值的類型有int,float,double,long等。
對于有小數的數值,開發人員首先想到的是可以用float或者double表示。比如求1.0和0.42兩個值的差,那么心算可得0.58,但是如果用Java來實際計算一下,那么其結果可能和我們預期的是不一樣的。
下面結合特殊場景,來給出Java常見的數值加減乘除運算示例,看看結果都是對的么?具體的示例代碼如下:
package com.jyd; //Java 數值計算精度問題探討示例 public class NumDemo { public static void main(String[] args) { int a = 10; int c0 = a / 3 ; System.out.println(c0);//3,只保留整數 //float和double大部分情況精確的,但有時會計算錯誤 // 在商業計算上(如金融、銀行,財務),不建議使用。 float b = 0.1f; float c2 = b / 3.0f;//0.033333335 System.out.println(c2); float b2 = 20019999; System.out.println(b2); //2.002E7 double d = 45.9D; double c3 = d / 3.0D ; System.out.println(c3);//15.299999999999999 (not 15.3) double e = 2001299.32D; double e2 = 0.11D; System.out.println(e + e2); //2001299.4300000002 System.out.println(1.0 - 0.42);//0.5800000000000001 System.out.println(4.015 * 100);//401.49999999999994 System.out.println(0.01 + 0.05);//0.060000000000000005 } }
上述示例代碼中,int類型的數值10,/ 除以3,實際返回的是int類型,即3,只保留整數部分。這個計算結果只要我們知道,一般都可以理解。Java中的浮點類型(如float和double)采用?IEEE 754?標準。float類型的值0.1除以3.0時,返回的還是float類型的值,按照數學角度來說,應該返回0.3333...(循環),但是Java程序返回0.033333335。float類型長度為32位,其中用?1?位表示數字的符號,用?8?位表示指數,用?23?位表示小數部分。感覺這個長度有的長,但是實際使用起來精度還是不夠。即使是64位的double也是如此。
示例中,20019999這個float類型的值,輸出后卻是2.002E7,即20020000,真是奇乎怪哉!,如果這個是銀行賬戶的金額,1個賬戶多1元,那么幾億個戶頭則多出了幾個億的金額,足見這個計算精度的問題是在特殊場景下,是一個不可接受的方案。 其他的示例涉及到加法、減法、乘法,也同樣會出現計算錯誤的問題。
按照官網的說法,float和double一般用于工程計算,而不能應用商業計算,如銀行,財務等。那么商業計算上,該如何解決此問題呢?此時需要用到一個特殊的類型,為BigDecimal。此類型是一個精確的類型,下面給出示例:
package com.jyd; import java.math.BigDecimal; import java.math.MathContext; //Java 數值計算精度問題探討示例 public class BigNumDemo { public static void main(String[] args) { BigDecimal b = new BigDecimal("0.1"); BigDecimal b1 = new BigDecimal("3.0"); BigDecimal c2 = b.divide(b1,10,BigDecimal.ROUND_DOWN) ;//0.0333333333 System.out.println(c2); BigDecimal b2 = new BigDecimal(""+20019999); System.out.println(b2); //20019999 BigDecimal d = new BigDecimal("45.9"); BigDecimal d2 = new BigDecimal("3.0"); BigDecimal c3 = d.divide(d2,10,BigDecimal.ROUND_CEILING) ; System.out.println(c3);//15.3000000000 BigDecimal e = new BigDecimal("2001299.32"); BigDecimal e2 = new BigDecimal("0.11"); System.out.println(e.add(e2)); //2001299.43 BigDecimal e3 = new BigDecimal("1.0"); BigDecimal e4 = new BigDecimal("0.42"); System.out.println(e3.subtract(e4));//0.58 BigDecimal e5 = new BigDecimal("4.015"); BigDecimal e6 = new BigDecimal("100"); System.out.println(e5.multiply(e6, MathContext.UNLIMITED));//401.500 BigDecimal e7 = new BigDecimal("0.01"); BigDecimal e8 = new BigDecimal("0.05"); System.out.println(e7.add(e8));//0.06 BigDecimal e9 = new BigDecimal("999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"); System.out.println(e9.add(e3));//1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0 } }
BigDecimal有多種構造函數,常用的有2種。建議使用String構造方式,不建議使用double構造方式,可能會出現精度丟失問題。通過上述示例可以看出,商業計算應該使用 BigDecimal類型來完成,雖然在加減乘除這塊使用起來有點不夠簡便,但是仍然比較容易理解。且操作字符串,也更容易操作。在乘除計算中,強制需要制定額外的參數,比如明確小數個數,四舍五入的模式等。
最后,Java編程的細節還有很多,比如不同精度的數值的賦值和轉換問題,float和double為什么會導致數據精度的丟失等,可以參考官網文檔或其他資料。這里不再贅述。
【奔跑吧!JAVA】有獎征文火熱進行中:
https://bbs.huaweicloud.com/blogs/265241
Java
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。