java 数字计算精度问题
編譯運(yùn)行下面這個(gè)程序會(huì)看到什么???
public class Test{ public static void main(String args[]){ 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); } };
你沒(méi)有看錯(cuò)!結(jié)果確實(shí)是??
0.060000000000000005??
0.5800000000000001??
401.49999999999994??
1.2329999999999999??
Java中的簡(jiǎn)單浮點(diǎn)數(shù)類型float和double不能夠進(jìn)行運(yùn)算。不光是Java,在其它很多編程語(yǔ)言中也有這樣的問(wèn)題。在大多數(shù)情況下,計(jì)算的結(jié)果是準(zhǔn)確的,但是多試幾次(可以做一個(gè)循環(huán))就可以試出類似上面的錯(cuò)誤。現(xiàn)在終于理解為什么要有BCD碼了。??
這個(gè)問(wèn)題相當(dāng)嚴(yán)重,如果你有9.999999999999元,你的計(jì)算機(jī)是不會(huì)認(rèn)為你可以購(gòu)買10元的商品的。??
在有的編程語(yǔ)言中提供了專門的貨幣類型來(lái)處理這種情況,但是Java沒(méi)有。現(xiàn)在讓我們看看如何解決這個(gè)問(wèn)題。
四舍五入??
我們的第一個(gè)反應(yīng)是做四舍五入。Math類中的round方法不能設(shè)置保留幾位小數(shù),我們只能象這樣(保留兩位):??
非常不幸,上面的代碼并不能正常工作,給這個(gè)方法傳入4.015它將返回4.01而不是4.02,如我們?cè)谏厦婵吹降??
4.015*100=401.49999999999994??
因此如果我們要做到精確的四舍五入,不能利用簡(jiǎn)單類型做任何運(yùn)算??
java.text.DecimalFormat也不能解決這個(gè)問(wèn)題:
輸出是4.02??
BigDecimal??
在《Effective?? Java》這本書中也提到這個(gè)原則,float和double只能用來(lái)做科學(xué)計(jì)算或者是工程計(jì)算,在商業(yè)計(jì)算中我們要用java.math.BigDecimal。BigDecimal一共有4個(gè)夠造方法,我們不關(guān)心用BigInteger來(lái)夠造的那兩個(gè),那么還有兩個(gè),它們是:?
上面的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.??
?? 可怕的.1,
原來(lái)我們?nèi)绻枰_計(jì)算,非要用String來(lái)夠造BigDecimal不可!在《Effective?? Java》一書中的例子是用String來(lái)夠造BigDecimal的,但是書上卻沒(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)方法,包括加減乘除和四舍五入:??
public?? static?? double?? add(double?? v1,double?? v2)??
public?? static?? double?? sub(double?? v1,double?? v2)??
public?? static?? double?? mul(double?? v1,double?? v2)??
public?? static?? double?? div(double?? v1,double?? v2)??
public?? static?? double?? div(double?? v1,double?? v2,int?? scale)??
public?? static?? double?? round(double?? v,int?? scale)??
?
附錄???
???
源文件Arith.java:
類 java.math.BigDecimal API
方法索引
abs()
返回一個(gè) BigDecimal ,其值是該數(shù)的絕對(duì)值,其標(biāo)度是 this.scale() 。
add(BigDecimal)
返回一個(gè) BigDecimal ,其值是 (this + val),其標(biāo)度是 MAX(this.scale(),val.scale) 。
compareTo(BigDecimal)
返回 -1、0 或 1,分別表示該數(shù)是小于、等于、或大于 val 。
divide(BigDecimal, int)
返回一個(gè) BigDecimal ,其值是 (this/val),其標(biāo)度是 this.scale() 。
divide(BigDecimal, int, int)
返回一個(gè) BigDecimal ,其值是 (this / val),其標(biāo)度是指定值 。
doubleValue()
把一個(gè)數(shù)字轉(zhuǎn)換為 double 型。
equals(Object)
如果 x 是一個(gè)等于該數(shù)字的 BigDecimal ,則返回 true。
floatValue()
把該數(shù)字轉(zhuǎn)換為 float 型。
hashCode()
計(jì)算該對(duì)象的散列碼。
intValue()
把該數(shù)字轉(zhuǎn)換為 int 值。
longValue()
把該數(shù)字轉(zhuǎn)換為 long 型。
max(BigDecimal)
返回 BigDecimal ,其值是 this 和 val 中的較大者。
min(BigDecimal)
返回 BigDecimal ,其值是 this 和 val 中的較小者。
movePointLeft(int)
返回一個(gè) BigDecimal ,其值等于該數(shù)十進(jìn)制小數(shù)點(diǎn)向左移動(dòng) n 位后所得的值。
movePointRight(int)
把十進(jìn)制小數(shù)點(diǎn)按指定數(shù)值向右移動(dòng)相應(yīng)位數(shù)。
multiply(BigDecimal)
返回一個(gè) BigDecimal ,其值是 (this * val),其標(biāo)度是 this.scale() + val.scale 。
negate()
返回一個(gè) BigDecimal ,其值是 -1 * val ,其標(biāo)度是 this.scale() 。
scale()
返回該數(shù)值的標(biāo)度。
setScale(int)
返回一個(gè) BigDecimal ,其標(biāo)度是指定值,其數(shù)值精確等于該數(shù)字的值。
setScale(int, int)
返回一個(gè) BigDecimal ,其標(biāo)度是指定值,其整數(shù)值是該 BigDecimal 的整數(shù)部分被 10 的適當(dāng)次冪(保持整個(gè)數(shù)值不變)乘或者除得到。
signum()
返回該數(shù)值的符號(hào)(即根據(jù)該數(shù)的值是正、零或負(fù)返回 -1 、 0 或 1 )。
subtract(BigDecimal)
返回一個(gè) BigDecimal ,其值是 (this - val),其標(biāo)度是 MAX(this.scale(),val.scale) 。
toBigInteger()
把該數(shù)字轉(zhuǎn)換為 BigInteger 。
toString()
返回表示該數(shù)字的字符串。
valueOf(long)
按照給定的值和零標(biāo)度返回一個(gè) BigDecimal 。
valueOf(long, int)
返回一個(gè) BigDecimal ,其值是 (val/10**scale)。
變量
ROUND_UP
public static final int ROUND_UP
總是在非零的舍棄小數(shù)(即截?cái)?之前增加數(shù)字。 注意該舍入模式不減少量值。 (從零開始舍入)
ROUND_DOWN
public static final int ROUND_DOWN
從不在舍棄的小數(shù)(即截?cái)?之前增加數(shù)字。 注意該舍入模式不增加量值。 (舍入到零)
ROUND_CEILING
public static final int ROUND_CEILING
如果 BigDecimal 為正,則作 ROUND_UP 操作;如果為負(fù),則作 ROUND_DOWN 操作。注意該舍入模式不減少值。(舍入到正無(wú)窮大)
ROUND_FLOOR
public static final int ROUND_FLOOR
如果 BigDecimal 為正,則作 ROUND_UP ;如果為負(fù),則作 ROUND_DOWN 。注意該舍入模式不增加值。(舍入到負(fù)無(wú)窮大)
ROUND_HALF_UP
public static final int ROUND_HALF_UP
若舍棄部分>=.5,則作 ROUND_UP ;否則,則作 ROUND_DOWN (舍入到 “最近的數(shù)值”,除非向上舍入和向下舍入的距離是相等的)。
ROUND_HALF_DOWN
public static final int ROUND_HALF_DOWN
若舍棄部分> .5 ,則作 ROUND_DOWN;否則,作 ROUND_DOWN 操作(舍入到 “最近的數(shù)值”,除非向下舍入和向上舍入的距離相等)。
ROUND_HALF_EVEN
public static final int ROUND_HALF_EVEN
如果舍棄部分左邊的數(shù)字為奇數(shù),則作 ROUND_HALF_UP 操作;如果它為偶數(shù),則作 ROUND_HALF_DOWN 操作(舍入到 “最近的數(shù)值”,除非向到兩邊的距離相等)。
ROUND_UNNECESSARY
public static final int ROUND_UNNECESSARY
該 “偽舍入模式”實(shí)際是要求操作有一個(gè)精確結(jié)果,,因此不需要舍入。如果該舍入模式對(duì)一個(gè)指定的操作產(chǎn)生不精確的結(jié)果,則拋出算術(shù)異常。
?
?
?
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的java 数字计算精度问题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 微服务架构设计模式~根据业务能力进行服务
- 下一篇: PDF签名系列(1):PDF签名机制的漏