本文先引入一個例子,星期天你和女朋友去逛街,看到一家奶茶店。女朋友想喝奶茶了,你就去買了杯奶茶,然後你問了一下價格。店員說奶茶0.9元一杯。然後你給了1元錢。這個時候你忽然問了一下女友。服務員該找我們多少錢呢?女友說你個小傻瓜當然是0.1元啊。作為一個”嚴謹“的程式猿,這時你拿起電腦寫了個簡單計算如下:
看到計算結果不是0.1這個時候你有點慌了,女朋友怎麼會錯呢?但是你沒有猶豫女朋友說的怎麼會錯呢,女朋友說的一定是對的。一定是電腦計算錯了,然後你毫不猶豫把電腦扔了。說了一句對對對該找0.1元。【PS奶茶目前當然不可能1元錢,本文為了演示計算效果所以定義奶茶1元,因為不是所有的浮點運算都會有這樣的問題】。
千萬別小看這些一點點的計算誤差,假如我說假如全國人民一人送你0.1元你能實現財富自由了【呸,別做白日夢了好好搬磚去吧】
好了聊文章的主題BigDecimal這個知識點,這個知識點應該很多人都知道了,我感覺很有有用就聊一下。阿里巴巴開發手冊中提到:
使用BigDecimal來定義值,再進行浮點數的運算操作。
注意:BigDecimal(double)存在精度損失的風險,在精確計算或比較的場景中可能會導致業務邏輯異常。如:BigDecimal g = new BigDecimal(0.1f)。實際儲存的值為:0.10000000149011611938 。
float a = 1.0f -0.9f; float b = 0.9f-0.8f ; System.out.println(a); System.out.println(b); System.out.println(a==b);
為什麼浮點數 float 或 double 運算的時候會有精度丟失的風險呢?
這個和計算機儲存浮點數的機制有很大關係。我們知道計算機是二進位制的,而且計算機在表示一個數字時,寬度是有限的,無限迴圈的小數儲存在計算機時,只能被截斷,所以就會導致小數精度發生損失的情況。
那怎麼解決精度丟失的問題呢?那就是本文要說的BigDecimal。用它可以實現對浮點的計算,解決精度丟失的問題。如下用差不多相同的程式碼結果就是對的【PS一定要記住女朋友說的就是對的就是0.1】。
BigDecimal建立方法,請先看注意事項:
阿里官方:【強制】禁止使用構造方法BigDecimal(double)的方式把double值轉換為BigDecimal物件
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = BigDecimal.valueOf(0.1);
開發中用到計算也就是加減乘除,BigDecimal中相關方法如下:
add 方法用於將兩個 BigDecimal 物件相加。subtract 方法用於將兩個 BigDecimal 物件相減。multiply 方法用於將兩個 BigDecimal 物件相乘,divide 方法用於將兩個 BigDecimal 物件相除。
這裡需要注意的是,在我們使用 divide 方法的時候儘量使用 3 個引數方法。其中 scale 表示要保留幾位小數,roundingMode 代表保留規則。具體可以看原始碼,原始碼中介紹的很清楚了,並且給了各種例子如下:
大小的比較
a.compareTo(b) : 返回 -1 說明 a 小於 b,0 說明a 等於 b , 1 說明 a 大於 b。用法如下:
注意:BigDecimal的比較不能使用equals進行比較【原因還是因為精度的問題】,一定要使用compareTo比較,如下:
BigDecilmal計算相關的工具類。
add方法:
public static BigDecimal add(Number v1, Number v2) { return add(new Number[]{v1, v2}); } public static BigDecimal add(Number... values) { if (ArrayUtil.isEmpty(values)) { return BigDecimal.ZERO; } Number value = values[0]; BigDecimal result = new BigDecimal(null == value ? "0" : value.toString()); for (int i = 1; i < values.length; i++) { value = values[i]; if (null != value) { result = result.add(new BigDecimal(value.toString())); } } return result; }
substract方法:
public static BigDecimal sub(Number v1, Number v2) { return sub(new Number[]{v1, v2}); } public static BigDecimal sub(Number... values) { if (ArrayUtil.isEmpty(values)) { return BigDecimal.ZERO; } Number value = values[0]; BigDecimal result = new BigDecimal(null == value ? "0" : value.toString()); for (int i = 1; i < values.length; i++) { value = values[i]; if (null != value) { result = result.subtract(new BigDecimal(value.toString())); } } return result; }
multiply 乘法:
public static BigDecimal mul(Number v1, Number v2) { return mul(new Number[]{v1, v2}); } public static BigDecimal mul(Number... values) { if (ArrayUtil.isEmpty(values)) { return BigDecimal.ZERO; } Number value = values[0]; BigDecimal result = new BigDecimal(null == value ? "0" : value.toString()); for (int i = 1; i < values.length; i++) { value = values[i]; result = result.multiply(new BigDecimal(null == value ? "0" : value.toString())); } return result; }
divide 除法:
public static BigDecimal div(BigDecimal v1, BigDecimal v2,Integer scale,RoundingMode roundingMode) { if (null == v1) { return BigDecimal.ZERO; } if (scale < 0) { scale = -scale; } return v1.divide(v2, scale, roundingMode); }
謝謝點贊,也歡迎你分享給其他的開發者,讓更多的人知道。當然也歡迎未關注的小夥伴關注。