www.日本精品,久久中文视频,中文字幕第一页在线播放,香蕉视频免费网站,老湿机一区午夜精品免费福利,91久久综合精品国产丝袜长腿,欧美日韩视频精品一区二区

java中Double類型運(yùn)算精度丟失問(wèn)題,(小數(shù)點(diǎn)多出99999999999999)x

發(fā)布時(shí)間:2020-09-07 來(lái)源: 精準(zhǔn)扶貧 點(diǎn)擊:

 java 中 中 Double 類型的運(yùn)算精度丟失的問(wèn)題 ( 小數(shù)點(diǎn)多出99999999999999)

 在使用 Java,double 進(jìn)行運(yùn)算時(shí),經(jīng)常出現(xiàn)精度丟失的問(wèn)題,總是在一個(gè)正確的結(jié)果左右偏 0.0000**1。

 特別在實(shí)際項(xiàng)目中,通過(guò)一個(gè)公式校驗(yàn)該值是否大于 0,如果大于 0 我們會(huì)做一件事情,小于 0 我們又處理其他事情。

 這樣的情況通過(guò)double 計(jì)算出來(lái)的結(jié)果去和 0 比較大小,尤其是有小數(shù)點(diǎn)的時(shí)候,經(jīng)常會(huì)因?yàn)榫葋G失而導(dǎo)致程序處理流程出錯(cuò)。

 首先貼一個(gè)使用的代碼:

 /**

  * 將 double 類型數(shù)據(jù)轉(zhuǎn)為字符串(如將 18.4 轉(zhuǎn)為 1840,如果需要 1840.0,把 int 強(qiáng)轉(zhuǎn)去掉即可)

  * @param d

  * @return

  */

 public static String double2String(double d){

 BigDecimal bg = new BigDecimal(d * 100);

 double doubleValue = bg.setScale(2,BigDecimal.ROUND_HALF_UP).doubleValue();

 return

 String.valueOf((int)doubleValue);

 }

  BigDecimal 在《Effective Java》這本書(shū)中也提到這個(gè)原則,float 和 double 只能用來(lái)做科學(xué)計(jì)算或者是工程計(jì)算,在商業(yè)計(jì)算中我們要用 java.math.BigDecimal。BigDecimal 一共有 4 個(gè)夠造方法,我們不關(guān)心用 BigInteger 來(lái)夠造的那兩個(gè),那么還有兩個(gè), 它們是:

 BigDecimal(double val)

 Translates a double into a BigDecimal.

 BigDecimal(String val)

 Translates the String repre sentation of a BigDecimal into a BigDecimal. 上面的 API 簡(jiǎn)要描述相當(dāng)?shù)拿鞔_,而且通常情況下,上面的那一個(gè)使用起來(lái)要方便一些。我們可能想都不想就用上了,會(huì)有什么問(wèn)題呢?等到出了問(wèn)題的時(shí)候,才發(fā)現(xiàn)上面哪個(gè)夠造方法的詳細(xì)說(shuō)明中有這么一段:

 Note: the results of this constructor can be somewhat unpredictable. One might assume that new BigDecimal(.1) is exactly equal to .1, but it is actually equal to .1000000000000000055511151231257827021181583404541015625. This is so because .1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of

  any finite length). Thus, the long value that is being passed in to the constructor is not exactly equal to .1, appearances nonwithstanding.

 The (String) constructor, on the other hand, is perfectly predictable: new BigDecimal(".1") is exactly equal to .1, as one would expect. Therefore, it is generally recommended that the (String) constructor be used in preference to this one. 原來(lái)我們?nèi)绻枰_計(jì)算,非要用 String 來(lái)夠造 BigDecimal 不可!在《Effective Java》一書(shū)中的例子是用 String 來(lái)夠造 BigDecimal 的,但是書(shū)上卻沒(méi)有強(qiáng)調(diào)這一點(diǎn),這也許是一個(gè)小小的失誤吧。

  解決方案 現(xiàn)在我們已經(jīng)可以解決這個(gè)問(wèn)題了,原則是使用 BigDecimal 并且一定要用 String來(lái)夠造。

 但是想像一下吧,如果我們要做一個(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ù)。你能夠忍受這么煩瑣的過(guò)程嗎?下面我們提供一個(gè)工具類 Arith 來(lái)簡(jiǎn)化操作。它 提供以下靜態(tài)方法,包括加減乘除和四舍五入:

 add(double v1,double v2) sub(double v1,double v2) mul(double v1,double v2) ) div(double v1,double v2,int scale) public static double round(double v,int scale)

  所以一般對(duì) double 類型進(jìn)行運(yùn)算時(shí),做好對(duì)結(jié)果進(jìn)行處理,然后拿這個(gè)值去做其他事情。

 使用如下:

 /**

  對(duì) double 數(shù)據(jù)進(jìn)行取精度.

  value

 double 數(shù)據(jù).

  scale

 精度位數(shù)(保留的小數(shù)位數(shù)).

  param roundingMode

 精度取值方式.

  * @return 精度計(jì)算后的數(shù)據(jù).

  */

 public static double round(double value, int scale,

 int roundingMode) {

  BigDecimal bd = new BigDecimal(value);

  bd = bd.setScale(scale, roundingMode);

  double d = bd.doubleValue();

  bd = null;

  return d;

 }

 /**

  double 相加

  1

  param d2

  * @return

  */

 public double sum(double d1,double d2){

 1 1

  BigDecimal bd2 = new BigDecimal(Double.toString(d2));

 return bd1.add(bd2).doubleValue();

 }

 /**

  double 相減

  1

  param d2

  * @return

  */

 public double sub(double d1,double d2){

 1 1

  BigDecimal bd2 = new BigDecimal(Double.toString(d2));

 return bd1.subtract(bd2).doubleValue();

 }

  /**

  double 乘法

  1

  param d2

  * @return

  */

 public double mul(double d1,double d2){

 1 1

  BigDecimal bd2 = new BigDecimal(Double.toString(d2));

 return bd1.multiply(bd2).doubleValue();

 }

 /**

  double 除法

  1

  * @param d2

  param scale 四舍五入 小數(shù)點(diǎn)位數(shù)

  * @return

  */

 public double div(double d1,double d2,int scale){

 當(dāng)然在此之前,你要判斷分母是否為 0,

  //

 為 0 你可以根據(jù)實(shí)際需求做相應(yīng)的處理

  1 1

  BigDecimal bd2 = new BigDecimal(Double.toString(d2));

 return bd1.divide

 (bd2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();

 }

 這樣,計(jì)算 double 類型的數(shù)據(jù)計(jì)算問(wèn)題就可以處理了。

 另外補(bǔ)充一下 JavaScript 四舍五入的方法:

 小數(shù)點(diǎn)問(wèn)題

  Math.round(totalAmount*100)/100 (保留 2 位)

  function formatFloat(src, pos)

 {

 return Math.round(src*Math.pow(10, pos))/Math.pow(10, pos);

  }

  四舍五入是我們小學(xué)的數(shù)學(xué)問(wèn)題,這個(gè)問(wèn)題對(duì)于我們程序猿來(lái)說(shuō)就類似于 1 到 10的加減乘除那么簡(jiǎn)單了。在講解之間我們先看如下一個(gè)經(jīng)典的案例:

 [java] view plaincopyprint? 1 public static void maiString[] args) {

  2

  12.5 的四舍五入值:" + Math.round(12.5));

  3

  System.out.println("-12.5 的四舍五入值:" + Math.round(-12.5));

  4

  }

  5 Output:

  6 12.5 的四舍五入值:13

  7. -12.5 的四舍五入值:-12

 這是四舍五入的經(jīng)典案例,也是我們參加校招時(shí)候經(jīng)常會(huì)遇到的(貌似我參加筆試的時(shí)候遇到過(guò)好多次)。從這兒結(jié)果中我們發(fā)現(xiàn)這兩個(gè)絕對(duì)值相同的數(shù)字,為何近似值會(huì)不同呢?其實(shí)這與 Math.round 采用的四舍五入規(guī)則來(lái)決定。

  四舍五入其實(shí)在金融方面運(yùn)用的非常多,尤其是銀行的利息。我們都知道銀行的盈利渠道主要是利息差,它從儲(chǔ)戶手里收集資金,然后放貸出去,期間產(chǎn)生的利息差就是銀行所獲得的利潤(rùn)。如果我們采用平常四舍五入的規(guī)則話,這里采用每10 筆存款利息計(jì)算作為模型,如下:

  四舍:0.000、0.001、0.002、0.003、0.004。這些舍的都是銀行賺的錢。

  五入:0.005、0.006、0.007、0.008、0.009。這些入的都是銀行虧的錢,分別為:0.005、0.004、.003、0.002、0.001。

  所以對(duì)于銀行來(lái)說(shuō)它的盈利應(yīng)該是 0.000 + 0.001 + 0.002 + 0.003 + 0.004 - 0.005 - 0.004 - 0.003 - 0.002 - 0.001 = -0.005。從結(jié)果中可以看出每 10 筆的利息銀行可能就會(huì)損失 0.005 元,千萬(wàn)別小看這個(gè)數(shù)字,這對(duì)于銀行來(lái)說(shuō)就是一筆非常大的損失。面對(duì)這個(gè)問(wèn)題就產(chǎn)生了如下的銀行家涉入法了。該算法是由美國(guó)銀行家提出了,主要用于修正采用上面四舍五入規(guī)則而產(chǎn)生的誤差。如下:

  舍去位的數(shù)值小于 5 時(shí),直接舍去。

  舍去位的數(shù)值大于 5 時(shí),進(jìn)位后舍去。

  當(dāng)舍去位的數(shù)值等于 5 時(shí),若 5 后面還有其他非 0 數(shù)值,則進(jìn)位后舍去,若 5后面是 0 時(shí),則根據(jù) 5 前一位數(shù)的奇偶性來(lái)判斷,奇數(shù)進(jìn)位,偶數(shù)舍去。

  對(duì)于上面的規(guī)則我們舉例說(shuō)明

 11.556 = 11.56 ------六入

 11.554 = 11.55 -----四舍

 11.5551 = 11.56 -----五后有數(shù)進(jìn)位

 11.545 = 11.54 -----五后無(wú)數(shù),若前位為偶數(shù)應(yīng)舍去

 11.555 = 11.56 -----五后無(wú)數(shù),若前位為奇數(shù)應(yīng)進(jìn)位

  下面實(shí)例是使用銀行家舍入法:

 [java] view plaincopyprint? 1 public static void main(String[] args) {

  2

  d = new BigDecimal(100000);

 //存款

  3.

  BigDecimal r = new BigDecimal(0.001875*3);

  //利息

 4.

  BigDecimal i = d.multiply(r).setScale(2,RoundingMode.HALF_EVEN);

  //使用銀行家算法

 5

 6

  System.out.println("季利息是:"+i);

  7

  }

  8 Output:

  9. 季利息是:562.50

  在上面簡(jiǎn)單地介紹了銀行家舍入法,目前 java 支持 7 中舍入法:

 1、 ROUND_UP:遠(yuǎn)離零方向舍入。向絕對(duì)值最大的方向舍入,只要舍棄位非0 即進(jìn)位。

 2、 ROUND_DOWN:趨向零方向舍入。向絕對(duì)值最小的方向輸入,所有的位都要舍棄,不存在進(jìn)位情況。

 3、 ROUND_CEILING:向正無(wú)窮方向舍入。向正最大方向靠攏。若是正數(shù),舍入行為類似于 ROUND_UP,若為負(fù)數(shù),舍入行為類似于 ROUND_DOWN。Math.round()方法就是使用的此模式。

 4、 ROUND_FLOOR:向負(fù)無(wú)窮方向舍入。向負(fù)無(wú)窮方向靠攏。若是正數(shù),舍入行為類似于 ROUND_DOWN;若為負(fù)數(shù),舍入行為類似于 ROUND_UP。

 5、 HALF_UP:最近數(shù)字舍入(5 進(jìn))。這是我們最經(jīng)典的四舍五入。

 6、 HALF_DOWN:最近數(shù)字舍入(5 舍)。在這里 5 是要舍棄的。

 7、 HAIL_EVEN:銀行家舍入法。

  提到四舍五入那么保留位就必不可少了,在 java 運(yùn)算中我們可以使用多種方式來(lái)實(shí)現(xiàn)保留位。

  保留位

  方法一:四舍五入 方法一:四舍五入

 [java] view plaincopyprint? 1 double

  f

  =

  111231.5585;

  2 BigDecimal

  b

  =

  new

  BigDecimal(f);

  3. double

  f1

  =

  b.setScale(2,

  RoundingMode.HALF_UP).doubleValue();

 在這里使用 BigDecimal ,并且采用 setScale 方法來(lái)設(shè)置精確度,同時(shí)使用RoundingMode.HALF_UP 表示使用最近數(shù)字舍入法則來(lái)近似計(jì)算。在這里我們可以看出 BigDecimal 和四舍五入是絕妙的搭配。

  方式二:

 方式二:

 [java] view plaincopyprint? 1 java.text.DecimalFormat

  df

  =new

  java.text.DecimalFormat(”#.00″);

  2. df.format(你要格式化的數(shù)字);

  例:new java.text.DecimalFormat(”#.00″).format(3.1415926)

  #.00 表示兩位小數(shù) #.0000 四位小數(shù) 以此類推…

  方式三:

 方式三:

 [java] view plaincopyprint? 1 double d = 3.1415926;

  2

 3 String result = String .format(”%.2f”);

  4

 5. %.2f %. 表示 小數(shù)點(diǎn)前任意位數(shù)

  2 表示兩位小數(shù) 格式后的結(jié)果為 f 表示浮點(diǎn)型。

  方式四:

 方式四:

  此外如果使用 struts 標(biāo)簽做輸出的話,有個(gè) format 屬性,設(shè)置為 format="0.00"就是保留兩位小數(shù)

  例如:

 [java] view plaincopyprint? 1 <bean:write name="entity" property="dkhAFSumPl"

 format="0.00" />

  2

 3 或者

  4

 5. <fmt:formatNumber type="number" value="${10000.22/100}" maxFractionDigits="0"/>

  6

 7. maxFractionDigits 表示保留的位數(shù)

  BigDecimal.setScale 處理 java 小數(shù)點(diǎn) BigDecimal.setScale()方法用于格式化小數(shù)點(diǎn) setScale(1)表示保留一位小數(shù),默認(rèn)用四舍五入方式

 DOWN)直接刪除多余的小數(shù)位,如 2.35 會(huì)變成 2.3

 UP)進(jìn)位處理,2.35 變成 2.4

 (1,BigDecimal.ROUND_HALF_UP)四舍五入,2.35 變成 2.4 setScaler(1,BigDecimal.ROUND_HALF_DOWN)四舍五入,2.35 變成 2.3,如果是 5則向下舍

  注釋:

 注釋:

 1:

 ale 指的是你小數(shù)點(diǎn)后的位數(shù)。比如 123.456 則 score 就是 3. score()就是 BigDecimal 類中的方法啊。

 比如:BigDecimal b = new BigDecimal("123.456"); b.scale(),返回的就是 3.

 2:

。

 roundingMode 是小數(shù)的保留模式。它們都是 BigDecimal 中的常量字段,有很多種。

 比如:BigDecimal.ROUND_HALF_UP 表示的就是 4 舍 5 入。

 3:

 pubilc BigDecimal divide(BigDecimal divisor, int scale, int roundingMode) 的意思是說(shuō):我用一個(gè) BigDecimal 對(duì)象除以 divisor 后的結(jié)果,并且要求這個(gè)結(jié)果保留有 scale 個(gè)小數(shù)位,roundingMode 表示的就是保留模式是什么,是四舍五入啊還是其它的,你可以自己選!

 4:對(duì)于一般 :對(duì)于一般 add 、subtract 、multiply 方法的小數(shù)位格式化如下:

 方法的小數(shù)位格式化如下:

 BigDecimal mData = new BigDecimal("9.655").setScale(2, BigDecimal.ROUND_HALF_UP);

  System.out.println("mData=" + mData);

  ----結(jié)果:

 結(jié)果:----- mData=9.66

相關(guān)熱詞搜索:小數(shù)點(diǎn) 多出 運(yùn)算

版權(quán)所有 蒲公英文摘 www.newchangjing.com