1. 程式人生 > >Java中2減1.1等於幾?

Java中2減1.1等於幾?

在Java中有一個奇怪的現象,我們編寫程式碼2-1.1,原本以為結果是非常明顯的0.9,但答案卻讓人大吃一驚,如圖:

原因在於:Java基本資料型別中的float、double型別的實質是浮點數,浮點數不能儲存精確的資料,這樣的話,浮點數在進行計算的時候,計算結果就不是精確的值。所以這裡得出經驗,在實際開發中,我們在對數值進行計算的時候,應儘量避免使用Java的+ - * / 等運算子,因為容易出現損失精度的問題,導致最終的計算結果錯誤。

尤其是在電商平臺中都是杜絕使用此種計算方式的,因為由於這種計算導致金額出現的誤差,所帶來的損失是無法估量的,也許就因為差一分錢,導致報表計算錯誤,客戶賬單對不上,都會帶來大麻煩。所以我們有必要找到一種正確的計算方法。

Java在java.math包中提供的API類BigDecimal,用來對超過16位有效位的數進行精確的運算。雙精度浮點型變數double可以處理16位有效數。在實際應用中,需要對更大或者更小的數進行運算和處理。float和double只能用來做科學計算或者是工程計算,在商業計算中要用java.math.BigDecimal。BigDecimal所建立的是物件,我們不能使用傳統的+、-、*、/等算術運算子直接對其物件進行數學運算,而必須呼叫其相對應的方法。

1、加法

2、減法

3、乘法

在上述的截圖中,會發現我們在做運算之前,都會將double值轉化為String值,儘管BigDecimal提供了多種構造方法,也是支援傳入double值的,但這裡我們並不推薦。因為傳入double值是容易出問題的,如圖:

a)引數型別為double的構造方法的結果有一定的不可預知性。有人可能認為在Java中寫入

newBigDecimal(0.1)所建立的BigDecimal正好等於 0.1(非標度值 1,其標度為 1),但是它實際上等於0.1000000000000000055511151231257827021181583404541015625。這是因為0.1無法準確地表示為 double(或者說對於該情況,不能表示為任何有限長度的二進位制小數)。這樣,傳入到構造方法的值不會正好等於 0.1(雖然表面上等於該值)。

b)另一方面,String 構造方法是完全可預知的:寫入 newBigDecimal("0.1") 將建立一個 BigDecimal,它正好等於預期的 0.1。因此,比較而言,通常建議優先使用String構造方法。

c)當double必須用作BigDecimal的源時,請注意,此構造方法提供了一個準確轉換;它不提供與以下操作相同的結果:先使用Double.toString(double)方法,然後使用BigDecimal(String)構造方法。將double轉換為String,也可以使用String的static方法:String.valueOf(double)。

4、除法

在使用除法時,我們發現divide方法中,我們多傳入了幾個引數。如果對於可以除盡的數不傳入這兩個引數也是沒有問題的,但如果我們使用100除以3會出現什麼問題呢?

我們發現報錯了,原因在於:通過BigDecimal的divide方法進行除法時當不整除,出現無限迴圈小數時,就會拋異常:java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result. 所以不管能不能除盡,我們都要防患於未然告訴運算器我們除不盡的方案是什麼。

BigDecimal已經幫助我們定義好了舍入模式,只有在作除法運算或四捨五入時才用到舍入模式。如下圖:

這麼多的四捨五入模式,我們在實際開發中應該使用那種呢,這個還是要分情況的。我倒是建議使用UNNECESSARY這種模式:計算結果是精確的,不需要四捨五入,那你可能會說這樣不就有問題了嗎?彆著急,待計算完成後我們再單獨對結果進行四捨五入?為什麼要這樣呢?因為我們不能保證我們的程式中只存在一次計算,如果我們有一個公式要連續對數值進行乘法和除法,每次都四捨五入,那計算結果才會有問題,但如果我們只對最終的結果值四捨五入就沒有問題了,因為多數在儲存的時候,資料庫都要求保留兩位小數。首先我們來看下不同的模式下計算的結果值有什麼差異?

在UNNECESSARY的時候最為精確。因為其保留的小數也最多。具體我們可以開啟原始碼自己看下原因。那最終我們如何將數值保留兩位小數呢?

例如: