日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

Java用BigDecimal類解決Double類型精度丟失的問題

瀏覽:113日期:2022-08-19 08:53:38
本篇要點

簡單描述浮點數十進制轉二進制精度丟失的原因。介紹幾種創建BigDecimal方式的區別。整理了高精度計算的工具類。學習了阿里巴巴Java開發手冊關于BigDecimal比較相等的規定。

經典問題:浮點數精度丟失

精度丟失的問題是在其他計算機語言中也都會出現,float和double類型的數據在執行二進制浮點運算的時候,并沒有提供完全精確的結果。產生誤差不在于數的大小,而是因為數的精度。

關于浮點數存儲精度丟失的問題,話題過于龐大,感興趣的同學可以自行搜索一下:【解惑】剖析float型的內存存儲和精度丟失問題

這里簡單討論一下十進制數轉二進制為什么會出現精度丟失的現象,十進制數分為整數部分和小數部分,我們分開來看看就知道原因為何:

十進制整數如何轉化為二進制整數?

將被除數每次都除以2,只要除到商為0就可以停止這個過程。

5 / 2 = 2 余 12 / 2 = 1 余 01 / 2 = 0 余 1 // 結果為 101

這個算法永遠都不會無限循環,整數永遠都可以使用二進制數精確表示,但小數呢?

十進制小數如何轉化為二進制數?

每次將小數部分乘2,取出整數部分,如果小數部分為0,就可以停止這個過程。

0.1 * 2 = 0.2 取整數部分00.2 * 2 = 0.4 取整數部分00.4 * 2 = 0.8 取整數部分00.8 * 2 = 1.6 取整數部分10.6 * 2 = 1.2 取整數部分10.2 * 2 = 0.4 取整數部分0 //... 我想寫到這就不必再寫了,你應該也已經發現,上面的過程已經開始循環,小數部分永遠不能為0

這個算法有一定概率會存在無限循環,即無法用有限長度的二進制數表示十進制的小數,這就是精度丟失問題產生的原因。

如何用BigDecimal解決double精度問題?

我們已經明白為什么精度會存在丟失現象,那么我們就應該知道,當某個業務場景對double數據的精度要求非常高時,就必須采取某種手段來處理這個問題,這也是BigDecimal為什么會被廣泛應用于金額支付場景中的原因啦。

BigDecimal類位于java.math包下,用于對超過16位有效位的數進行精確的運算。一般來說,double類型的變量可以處理16位有效數,但實際應用中,如果超過16位,就需要BigDecimal類來操作。

既然這樣,那用BigDecimal就能夠很好解決這個問題咯?

public static void main(String[] args) {// 方法1 BigDecimal a = new BigDecimal(0.1); System.out.println('a --> ' + a);// 方法2 BigDecimal b = new BigDecimal('0.1'); System.out.println('b --> ' + b);// 方法3 BigDecimal c = BigDecimal.valueOf(0.1); System.out.println('c --> ' + c); }

你可以思考一下,控制臺輸出會是啥。

a --> 0.1000000000000000055511151231257827021181583404541015625b --> 0.1c --> 0.1

可以看到,使用方法一的構造函數仍然出現了精度丟失的問題,而方法二和方法三符合我們的預期,為什么會這樣呢?

這三個方法其實對應著三種不同的構造函數:

// 傳入doublepublic BigDecimal(double val) { this(val,MathContext.UNLIMITED); }// 傳入string public BigDecimal(String val) { this(val.toCharArray(), 0, val.length()); } public static BigDecimal valueOf(double val) { // Reminder: a zero double returns ’0.0’, so we cannot fastpath // to use the constant ZERO. This might be important enough to // justify a factory approach, a cache, or a few private // constants, later. // 可以看到實際上就是第二種 return new BigDecimal(Double.toString(val)); }

關于這三個構造函數,JDK已經給出了解釋,并用Notes標注:

Java用BigDecimal類解決Double類型精度丟失的問題

為了防止以后圖片可能會存在顯示問題,這里再記錄一下:

new BigDecimal(double val)

該方法是不可預測的,以0.1為例,你以為你傳了一個double類型的0.1,最后會返回一個值為0.1的BigDecimal嗎?不會的,原因在于,0.1無法用有限長度的二進制數表示,無法精確地表示為雙精度數,最后的結果會是0.100000xxx。

new BigDecimal(String val)

該方法是完全可預測的,也就是說你傳入一個字符串'0.1',他就會給你返回一個值完全為0,1的BigDecimal,官方也表示,能用這個構造函數就用這個構造函數叭。

BigDecimal.valueOf(double val)

第二種構造方式已經足夠優秀,可你還是想傳入一個double值,怎么辦呢?官方其實提供給你思路并且實現了它,可以使用Double.toString(double val)先將double值轉為String,再調用第二種構造方式,你可以直接使用靜態方法:valueOf(double val)。

Double的加減乘除運算工具類

BigDecimal所創建的是對象,故我們不能使用傳統的+、-、*、/等算術運算符直接對其對象進行數學運算,而必須調用其相對應的方法。方法中的參數也必須是BigDecimal的對象。網上有很多這樣的工具類,這邊直接貼一下,邏輯不難,主要為了簡化項目中頻繁互相轉化的問題。

/** * 用于高精確處理常用的數學運算 */public class ArithmeticUtils { //默認除法運算精度 private static final int DEF_DIV_SCALE = 10; /** * 提供精確的加法運算 * * @param v1 被加數 * @param v2 加數 * @return 兩個參數的和 */ public static double add(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.add(b2).doubleValue(); } /** * 提供精確的加法運算 * * @param v1 被加數 * @param v2 加數 * @return 兩個參數的和 */ public static BigDecimal add(String v1, String v2) { BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); return b1.add(b2); } /** * 提供精確的加法運算 * * @param v1 被加數 * @param v2 加數 * @param scale 保留scale 位小數 * @return 兩個參數的和 */ public static String add(String v1, String v2, int scale) { if (scale < 0) { throw new IllegalArgumentException( 'The scale must be a positive integer or zero'); } BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); return b1.add(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString(); } /** * 提供精確的減法運算 * * @param v1 被減數 * @param v2 減數 * @return 兩個參數的差 */ public static double sub(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.subtract(b2).doubleValue(); } /** * 提供精確的減法運算。 * * @param v1 被減數 * @param v2 減數 * @return 兩個參數的差 */ public static BigDecimal sub(String v1, String v2) { BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); return b1.subtract(b2); } /** * 提供精確的減法運算 * * @param v1 被減數 * @param v2 減數 * @param scale 保留scale 位小數 * @return 兩個參數的差 */ public static String sub(String v1, String v2, int scale) { if (scale < 0) { throw new IllegalArgumentException( 'The scale must be a positive integer or zero'); } BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); return b1.subtract(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString(); } /** * 提供精確的乘法運算 * * @param v1 被乘數 * @param v2 乘數 * @return 兩個參數的積 */ public static double mul(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.multiply(b2).doubleValue(); } /** * 提供精確的乘法運算 * * @param v1 被乘數 * @param v2 乘數 * @return 兩個參數的積 */ public static BigDecimal mul(String v1, String v2) { BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); return b1.multiply(b2); } /** * 提供精確的乘法運算 * * @param v1 被乘數 * @param v2 乘數 * @param scale 保留scale 位小數 * @return 兩個參數的積 */ public static double mul(double v1, double v2, int scale) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return round(b1.multiply(b2).doubleValue(), scale); } /** * 提供精確的乘法運算 * * @param v1 被乘數 * @param v2 乘數 * @param scale 保留scale 位小數 * @return 兩個參數的積 */ public static String mul(String v1, String v2, int scale) { if (scale < 0) { throw new IllegalArgumentException( 'The scale must be a positive integer or zero'); } BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); return b1.multiply(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString(); } /** * 提供(相對)精確的除法運算,當發生除不盡的情況時,精確到 * 小數點以后10位,以后的數字四舍五入 * * @param v1 被除數 * @param v2 除數 * @return 兩個參數的商 */ public static double div(double v1, double v2) { return div(v1, v2, DEF_DIV_SCALE); } /** * 提供(相對)精確的除法運算。當發生除不盡的情況時,由scale參數指 * 定精度,以后的數字四舍五入 * * @param v1 被除數 * @param v2 除數 * @param scale 表示表示需要精確到小數點以后幾位。 * @return 兩個參數的商 */ public static double div(double v1, double v2, int scale) { if (scale < 0) { throw new IllegalArgumentException('The scale must be a positive integer or zero'); } BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue(); } /** * 提供(相對)精確的除法運算。當發生除不盡的情況時,由scale參數指 * 定精度,以后的數字四舍五入 * * @param v1 被除數 * @param v2 除數 * @param scale 表示需要精確到小數點以后幾位 * @return 兩個參數的商 */ public static String div(String v1, String v2, int scale) { if (scale < 0) { throw new IllegalArgumentException('The scale must be a positive integer or zero'); } BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v1); return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).toString(); } /** * 提供精確的小數位四舍五入處理 * * @param v 需要四舍五入的數字 * @param scale 小數點后保留幾位 * @return 四舍五入后的結果 */ public static double round(double v, int scale) { if (scale < 0) { throw new IllegalArgumentException('The scale must be a positive integer or zero'); } BigDecimal b = new BigDecimal(Double.toString(v)); return b.setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue(); } /** * 提供精確的小數位四舍五入處理 * * @param v 需要四舍五入的數字 * @param scale 小數點后保留幾位 * @return 四舍五入后的結果 */ public static String round(String v, int scale) { if (scale < 0) { throw new IllegalArgumentException( 'The scale must be a positive integer or zero'); } BigDecimal b = new BigDecimal(v); return b.setScale(scale, BigDecimal.ROUND_HALF_UP).toString(); } /** * 取余數 * * @param v1 被除數 * @param v2 除數 * @param scale 小數點后保留幾位 * @return 余數 */ public static String remainder(String v1, String v2, int scale) { if (scale < 0) { throw new IllegalArgumentException( 'The scale must be a positive integer or zero'); } BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); return b1.remainder(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString(); } /** * 取余數 BigDecimal * * @param v1 被除數 * @param v2 除數 * @param scale 小數點后保留幾位 * @return 余數 */ public static BigDecimal remainder(BigDecimal v1, BigDecimal v2, int scale) { if (scale < 0) { throw new IllegalArgumentException( 'The scale must be a positive integer or zero'); } return v1.remainder(v2).setScale(scale, BigDecimal.ROUND_HALF_UP); } /** * 比較大小 * 阿里巴巴開發規范明確:比較BigDecimal的等值需要使用compareTo,不可用equals * equals會比較值和精度,compareTo會忽略精度 * @param v1 被比較數 * @param v2 比較數 * @return 如果v1 大于v2 則 返回true 否則false */ public static boolean compare(String v1, String v2) { BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); int bj = b1.compareTo(b2); boolean res; if (bj > 0) res = true; else res = false; return res; }}阿里巴巴Java開發手冊關于BigDecimal的規定

【強制】如上所示BigDecimal的等值比較應使用compareTo()方法,而不是equals()方法。

說明:equals()方法會比較值和精度(1.0和1.00返回結果為false),而compareTo()則會忽略精度。

關于這一點,我們來看一個例子就明白了:

public static void main(String[] args) { BigDecimal a = new BigDecimal('1'); BigDecimal b = new BigDecimal('1.0'); System.out.println(a.equals(b)); // false System.out.println(a.compareTo(b)); //0 表示相等 }

JDK中對這兩個方法的解釋是這樣的:

使用compareTo方法,兩個值相等但是精度不同的BigDecimal對象會被認為是相等的,比如2.0和2.00。建議使用x.compareTo(y) <op> 0來表示(<, == , > , >= , != , <=)中的其中一個關系,<op>就表示運算符。 equals方法與compareTo方法不同,此方法僅在兩個BigDecimal對象的值和精度都相等時才被認為是相等的,如2.0和2.00就是不相等的。

以上就是Java用BigDecimal類解決Double類型精度丟失的問題的詳細內容,更多關于Java BigDecimal解決Double類型精度丟失的資料請關注好吧啦網其它相關文章!

標簽: Java
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
精品一区二区三区的国产在线观看 | 欧美在线观看天堂一区二区三区| 亚洲精品国产偷自在线观看| 韩国精品主播一区二区在线观看 | 国产精品一区二区免费福利视频 | 99视频在线精品国自产拍免费观看| 日本午夜精品久久久| 另类激情亚洲| 国产毛片一区二区三区| 97精品在线| 丝袜美腿高跟呻吟高潮一区| 国产午夜一区| 国产91在线播放精品| 国产美女精品| 91精品啪在线观看国产爱臀| 首页国产欧美久久| 97精品一区二区| 日韩精品电影一区亚洲| 精品久久亚洲| 亚洲午夜免费| 国产一区二区色噜噜| 黄在线观看免费网站ktv| 久久午夜精品一区二区| 国产精品va视频| 一区二区高清| 麻豆国产精品一区二区三区| 国产精品xxx在线观看| 欧美日韩国产欧| 日本欧美国产| 亚洲va久久久噜噜噜久久| 日韩国产激情| 88久久精品| 午夜日韩福利| 日韩欧美自拍| 国产传媒在线| 久久一区亚洲| 日韩精品一区二区三区中文在线| 999国产精品视频| 精品亚洲精品| 少妇精品久久久一区二区| 精品香蕉视频| 国产精品亚洲欧美日韩一区在线| 亚洲成人精品| 国产精品视频一区二区三区综合| 激情欧美国产欧美| 国产精品福利在线观看播放| 亚洲美女91| 黄色成人91| 久久久久午夜电影| 精品一区av| 麻豆久久久久久| 三级欧美韩日大片在线看| 久久久久美女| 久久久9色精品国产一区二区三区| 国产精品一国产精品| 国产欧美一区二区三区米奇| 日韩精品亚洲专区| 日韩毛片网站| 欧美日韩一视频区二区| 日韩高清二区| 日韩av资源网| 国产亚洲一区二区三区啪| 91欧美极品| 国产九一精品| 国产激情精品一区二区三区| 国产精品成人3p一区二区三区| 伊人国产精品| 日韩激情av在线| 国产伦精品一区二区三区视频| 国产日产精品一区二区三区四区的观看方式| 午夜性色一区二区三区免费视频| 亚洲一区有码| 国产精品久久久亚洲一区| 久久亚洲人体| 五月精品视频| 亚洲精品欧洲| 亚洲三级网址| 亚洲精品在线二区| 国产伦精品一区二区三区视频 | 日本免费在线视频不卡一不卡二| 日韩超碰人人爽人人做人人添| 久久激情五月激情| 成人在线免费观看91| 国产真实久久| 日韩av不卡在线观看| 亚洲免费福利| 蜜桃传媒麻豆第一区在线观看| 91亚洲精品视频在线观看| 婷婷精品视频| 国产精品久久久久久久久久齐齐 | 欧美日韩1区2区3区| 欧美91在线|欧美| 亚洲v在线看| 日本午夜精品久久久| 日韩欧美二区| 日本中文字幕不卡| 久草免费在线视频| 中文字幕一区二区三区四区久久| 午夜天堂精品久久久久| 高清不卡亚洲| 日韩精品亚洲专区| 欧美亚洲在线日韩| 国产欧美一区二区三区国产幕精品| 国产精品精品国产一区二区| 丝袜美腿亚洲色图| av亚洲一区二区三区| 88久久精品| 欧美日韩视频| 国产66精品| 日韩精品国产精品| 制服诱惑一区二区| 久久久久国产精品一区二区| 777久久精品| 一区视频在线| 日韩欧美网址| 国产成人久久精品麻豆二区 | 午夜影院欧美| 激情综合五月| 久久99性xxx老妇胖精品| 亚洲精品四区| 99国产精品99久久久久久粉嫩| 四虎4545www国产精品| 久久久久亚洲精品中文字幕| 青草国产精品| 中文欧美日韩| 欧美在线综合| 丝袜国产日韩另类美女| 亚洲深夜av| 欧美专区在线| 综合欧美精品| 欧美精品三级在线| 久久精品一区| 蜜臀国产一区| 久久精品亚洲人成影院| 中文字幕在线视频网站| 首页国产精品| 日韩欧美1区| 国产精品久久久久久久久久久久久久久| 亚洲黄色在线| 国产在线观看www| 高清av不卡| 午夜久久影院| 国产精品毛片一区二区三区| 免费日韩一区二区| 在线日韩成人| 欧美日韩午夜电影网| 国产精品白丝一区二区三区| 久久精品九色| 免费不卡中文字幕在线| 免费久久99精品国产| 欧美亚洲一级| 免费一二一二在线视频| 日韩一级不卡| 国产精品tv| 日韩欧美另类一区二区| 午夜一区在线| 久久99久久久精品欧美| 欧美日韩一二三四| 亚洲最新av| 日韩免费看片| 日韩国产在线不卡视频| 国产精品13p| 亚洲精品福利| 精品丝袜在线| 日韩三级久久| 在线日韩一区| 国产丝袜一区| 麻豆一区二区三区| 精品入口麻豆88视频| 日韩不卡在线| 欧美xxxx性| 亚洲免费中文| 精品高清久久| 亚洲精品电影| 日韩黄色在线观看| 久久国产精品免费一区二区三区| 日韩在线a电影| 蜜臀av性久久久久蜜臀aⅴ四虎| 亚洲高清影视| 欧美影院三区| 亚洲一级黄色| 午夜在线一区二区| 首页亚洲欧美制服丝腿| 免费在线看一区| 亚洲在线一区| 日本一区二区三区视频在线看| 一区二区三区网站| 丝袜美腿亚洲色图| 亚洲小说春色综合另类电影| 国产毛片久久| 日韩av二区在线播放| 国内在线观看一区二区三区| 亚洲综合精品| 国产国产精品| 国产精品视频3p| 日韩精品久久久久久久电影99爱| 日韩精品三级| 国产伊人久久| 国产精品日本欧美一区二区三区| 亚洲毛片视频|