日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

关于不能够精确的对浮点数进行运算的问题

發(fā)布時(shí)間:2025/4/16 编程问答 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 关于不能够精确的对浮点数进行运算的问题 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

http://edu.eoe.cn/?? 在線課堂

昨天看到一篇帖子說了幾個(gè)很明顯的簡(jiǎn)單的浮點(diǎn)的運(yùn)算,計(jì)算機(jī)都會(huì)算錯(cuò)。
我引過來給大家看看:‘
運(yùn)行代碼:

System.out.println(0.05 + 0.01);System.out.println(1.0 - 0.42);System.out.println(4.015 * 100);System.out.println(123.3 / 100);

大家可以自己新建一個(gè)工程來試試,結(jié)果如下:

0.060000000000000005 0.5800000000000001 401.49999999999994 1.2329999999999999

這就很神奇了,0.05+0.01很明顯是0.06嘛,但是為什么會(huì)變成0.060000000000000005?
是不是計(jì)算器太弱智?真是計(jì)算機(jī)就不靠譜了?

當(dāng)然不是的。主要是因?yàn)閖ava中的簡(jiǎn)單類型并不適用于對(duì)浮點(diǎn)的精確計(jì)算。不光是JAVA ,其它語(yǔ)言也存在同樣的問題。
雖然現(xiàn)在CPU都支持浮點(diǎn)的運(yùn)算了,但是CPU在處理的時(shí)候,也是先把浮點(diǎn)數(shù)(float , double)轉(zhuǎn)成整數(shù)再轉(zhuǎn)成二進(jìn)制,然后進(jìn)行操作,如果有取余,會(huì)有不同的取余方式。
再加上運(yùn)算完成后,再多二進(jìn)制轉(zhuǎn)成上層的浮點(diǎn),又會(huì)有一些取舍。就造成了呈現(xiàn)出來時(shí)的簡(jiǎn)單明顯的錯(cuò)誤 。

所以說,一般float和double用來做科學(xué)計(jì)算或者是工程計(jì)算,在一般對(duì)精度要求較高的地方(如商業(yè)),我們會(huì)用到BCD碼或者是java.math.BigDecimal。

BSD碼(Binary-Coded Decimal),稱BCD碼或二-十進(jìn)制代碼,亦稱二進(jìn)碼十進(jìn)數(shù)。是一種二進(jìn)制的數(shù)字編碼形式,用二進(jìn)制編碼的十進(jìn)制代碼。
大家對(duì)這個(gè)有興趣的可以深入研究一下,今天我們主要講的是BigDecimal類。因?yàn)槲覀冊(cè)谧鲰?xiàng)目的時(shí)候,尤其是對(duì)商業(yè)項(xiàng)目時(shí),我們不可能還去搞個(gè)什么BSD碼,可以直接利用BigDecimal類來完成我們的需求。

BigDecimal 是Java提供的不可變的、任意精度的有符號(hào)十進(jìn)制數(shù)。如果想看更多關(guān)于BigDecimal的介紹,大家可以自行去查看JDK的文檔。

BigDecimal提供了一系列的構(gòu)造函數(shù),主用于將double , string等轉(zhuǎn)化成BigDecimal對(duì)象。

BigDecimal(double val) 將 double 轉(zhuǎn)換為 BigDecimal,后者是 double 的二進(jìn)制浮點(diǎn)值準(zhǔn)確的十進(jìn)制表示形式。 BigDecimal(String val) 將 BigDecimal 的字符串表示形式轉(zhuǎn)換為 BigDecimal。

習(xí)慣上我們?cè)谑褂酶↑c(diǎn)數(shù)的時(shí)候都是直接定義的double , float數(shù)據(jù)類型,在定義上是沒有問題,但是如果我們直接調(diào)用BigDecimal(double val) 方法來轉(zhuǎn)化,那我們可得先注意一下JDK文檔中關(guān)于這個(gè)構(gòu)造的詳細(xì)說明了:

直接上中文了,英文好的,可以自己去看原版:

注:
此構(gòu)造方法的結(jié)果有一定的不可預(yù)知性。有人可能認(rèn)為在 Java 中寫入 new BigDecimal(0.1) 所創(chuàng)建的 BigDecimal 正好等于 0.1(非標(biāo)度值 1,其標(biāo)度為 1),但是它實(shí)際上等于 0.1000000000000000055511151231257827021181583404541015625。這是因?yàn)?0.1 無法準(zhǔn)確地表示為 double(或者說對(duì)于該情況,不能表示為任何有限長(zhǎng)度的二進(jìn)制小數(shù))。這樣,傳入 到構(gòu)造方法的值不會(huì)正好等于 0.1(雖然表面上等于該值)。

BigDecimal(double val) 這個(gè)方法是不可預(yù)知的,所以我們推薦使用BigDecimal(String val) 。
String的構(gòu)造函數(shù)就是可預(yù)知的,new BigDecimal(“.1”)如同期望的那樣精確的等于.1。

接下來我們對(duì) 0.05+0.01重新修改一下:

BigDecimal bd1 = new BigDecimal(Double.toString(0.05));BigDecimal bd2 = new BigDecimal(Double.toString(0.01));System.out.println(bd1.add( bd2));

我們?cè)賮砜纯催\(yùn)行的結(jié)果:

0.06

這下就是我們想要的結(jié)果了,完成了高精度的一個(gè)運(yùn)算了。

注意:
?現(xiàn)在我們已經(jīng)知道怎么樣來解決這個(gè)問題了,原則上是推薦使用BigDecimal(String val) 構(gòu)造方法。
我建議,在商業(yè)的應(yīng)用中,涉及到money的浮點(diǎn)運(yùn)算全都定義成String ,在數(shù)據(jù)庫(kù)中保存也是String ,在需要使用到這個(gè)money來作運(yùn)算的時(shí)候,我們?cè)侔裇tring轉(zhuǎn)化成BigDecimal來完成高精度的運(yùn)算。
????????
試想一下,如果我們要做一個(gè)加法運(yùn)算,需要先將兩個(gè)浮點(diǎn)數(shù)轉(zhuǎn)為String,然后夠造成BigDecimal,在其中一個(gè)上調(diào)用add方法,傳入另一個(gè)作為參數(shù),然后把運(yùn)算的結(jié)果(BigDecimal)再轉(zhuǎn)換為浮點(diǎn)數(shù)。你能夠忍受這么煩瑣的過程嗎?

SO, 網(wǎng)上找了一個(gè)比較好的一個(gè)工具類,封閉了簡(jiǎn)單的操作??梢詤⒖家幌?#xff1a;

/*** * 由于Java的簡(jiǎn)單類型不能夠精確的對(duì)浮點(diǎn)數(shù)進(jìn)行運(yùn)算,這個(gè)工具類提供精* 確的浮點(diǎn)數(shù)運(yùn)算,包括加減乘除和四舍五入。* **/ public class Arith{//默認(rèn)除法運(yùn)算精度private static final int DEF_SCALE = 10;//這個(gè)類不能實(shí)例化private Arith(){}/*** 提供精確的加法運(yùn)算。* @param v1 被加數(shù)* @param v2 加數(shù)* @return 兩個(gè)參數(shù)的和*/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();}/*** 提供精確的減法運(yùn)算。* @param v1 被減數(shù)* @param v2 減數(shù)* @return 兩個(gè)參數(shù)的差*/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();} /*** 提供精確的乘法運(yùn)算。* @param v1 被乘數(shù)* @param v2 乘數(shù)* @return 兩個(gè)參數(shù)的積*/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();}/*** 提供(相對(duì))精確的除法運(yùn)算,當(dāng)發(fā)生除不盡的情況時(shí),精確到* 小數(shù)點(diǎn)以后10位,以后的數(shù)字四舍五入。* @param v1 被除數(shù)* @param v2 除數(shù)* @return 兩個(gè)參數(shù)的商*/public static double div(double v1,double v2){return div(v1,v2,DEF_SCALE);}/*** 提供(相對(duì))精確的除法運(yùn)算。當(dāng)發(fā)生除不盡的情況時(shí),由scale參數(shù)指* 定精度,以后的數(shù)字四舍五入。* @param v1 被除數(shù)* @param v2 除數(shù)* @param scale 表示表示需要精確到小數(shù)點(diǎn)以后幾位。* @return 兩個(gè)參數(shù)的商*/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();}/*** 提供精確的小數(shù)位四舍五入處理。* @param v 需要四舍五入的數(shù)字* @param scale 小數(shù)點(diǎn)后保留幾位* @return 四舍五入后的結(jié)果*/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));BigDecimal one = new BigDecimal("1");return b.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue();} }

?首發(fā)地址:http://www.eoeandroid.com/thread-230579-1-1.html

另外一個(gè)地址:http://krislq.com/150?? 嘎嘎!

轉(zhuǎn)載于:https://www.cnblogs.com/nuliniaoboke/archive/2012/11/21/2780557.html

總結(jié)

以上是生活随笔為你收集整理的关于不能够精确的对浮点数进行运算的问题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。