咱們都知道,計算機是使用二進制存儲數據的。而日常生活中,大多數狀況下咱們都是使用的十進制,所以計算機顯示給咱們看的內容大多數也是十進制的,這就使得不少時候數據須要在二進制與十進制之間進行轉換。對於整數來講,兩種進制能夠作到一一對應。而對於小數來說就不是這樣的啦。java
咱們先來看看十進制小數轉二進制小數的方法ide
對小數點之後的數乘以2,會獲得一個結果,取結果的整數部分(不是1就是0),而後再用小數部分再乘以2,再取結果的整數部分……以此類推,直到小數部分爲0或者位數已經夠了。順序取每次運算獲得的整數部分,即爲轉換後的小數部分。工具
演示: 0.125 ×2=0.25 .......................0 0.25×2=0.5.............................0 0.5×2=1.0................................1 即 0.125的二進制表示爲小數部分爲0.001
其實咱們能夠看出,這種方法實質上就是用1/2,1/4,8/1...來組合加出咱們要轉換的數據值,但顯然不是全部的數都可以組合出來的。如0.1。測試
0.1×2=0.2 .....................0 0.2×2=0.4 ......................0 0.4×2=0.8 .....................0 0.8×2=1.6.......................1 0.6×2=1.2.......................1 0.2×2=0.4.......................0 .....
從上述計算過程咱們能夠看出,這是個無限小數,因此在這種狀況下咱們的float、double只能捨去一些位。優化
那爲何咱們在直接給float賦值在輸出時沒有看到精度損失而在運算時卻會出現呢?code
確實是這樣,以下ip
float a = 0.2f; System.out.println(a); //輸出0.2
對於上述狀況我只是查了資料,好像是由於編譯器會進行優化,當咱們存儲的數據特別接近的時候,編譯器會很貼心的返回咱們想看到的數值(即二進制浮點數並不能準確的表示0.1這個十進制小數,它使用了0.100000001490116119384765625來代替0.1。),至於到了運算中,就會出現精度損失較大從而看到了真相。若是這塊說的不對歡迎小夥伴們在評論區指正!ci
咱們通常會使用
BigDecimal 來避免出現精度丟失問題,至於爲何BigDecimal 能夠避免,而float或double不行,咱們在此不詳細討論,簡單來講就是BigDecimal 經過藉助整數來表示小數的方式,由於對於整數而言,二進制和十進制是徹底一一對應的,用整數來表示小數,再記錄下小數的位數,就能夠完美的解決該問題。編譯器
java.math.BinInteger 類和 java.math.BigDecimal 類都是Java提供的用於高精度計算的類.其中 BigInteger 類是針對大整數的處理類,而 BigDecimal 類則是針對大小數的處理類.源碼
BigDecimal BigDecimal(double d); //不容許使用 BigDecimal BigDecimal(String s); //經常使用,推薦使用 static BigDecimal valueOf(double d); //經常使用,推薦使用
測試
System.out.println(new BigDecimal(0.1)); System.out.println(BigDecimal.valueOf(0.1)); \\輸出***************************************** 0.1000000000000000055511151231257827021181583404541015625 0.1
咱們經過一個工具類源碼來體會BigDecimal的常規用法
package com.util; import java.math.BigDecimal; /** * 提供精確的浮點數運算(包括加、減、乘、除、四捨五入)工具類 */ public class ArithUtil { // 除法運算默認精度 private static final int DEF_DIV_SCALE = 10; private ArithUtil() { } /** * 精確加法 */ public static double add(double value1, double value2) { BigDecimal b1 = BigDecimal.valueOf(value1); BigDecimal b2 = BigDecimal.valueOf(value2); return b1.add(b2).doubleValue(); } /** * 精確減法 */ public static double sub(double value1, double value2) { BigDecimal b1 = BigDecimal.valueOf(value1); BigDecimal b2 = BigDecimal.valueOf(value2); return b1.subtract(b2).doubleValue(); } /** * 精確乘法 */ public static double mul(double value1, double value2) { BigDecimal b1 = BigDecimal.valueOf(value1); BigDecimal b2 = BigDecimal.valueOf(value2); return b1.multiply(b2).doubleValue(); } /** * 精確除法 使用默認精度 */ public static double div(double value1, double value2) throws IllegalAccessException { return div(value1, value2, DEF_DIV_SCALE); } /** * 精確除法 * @param scale 精度 */ public static double div(double value1, double value2, int scale) throws IllegalAccessException { if(scale < 0) { throw new IllegalAccessException("精確度不能小於0"); } BigDecimal b1 = BigDecimal.valueOf(value1); BigDecimal b2 = BigDecimal.valueOf(value2); // return b1.divide(b2, scale).doubleValue(); return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue(); } /** * 四捨五入 * @param scale 小數點後保留幾位 */ public static double round(double v, int scale) throws IllegalAccessException { return div(v, 1, scale); } /** * 比較大小 */ public static boolean equalTo(BigDecimal b1, BigDecimal b2) { if(b1 == null || b2 == null) { return false; } return 0 == b1.compareTo(b2); } }