java 数字计算精度问题
編譯運行下面這個程序會看到什么???
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); } };
你沒有看錯!結果確實是??
0.060000000000000005??
0.5800000000000001??
401.49999999999994??
1.2329999999999999??
Java中的簡單浮點數類型float和double不能夠進行運算。不光是Java,在其它很多編程語言中也有這樣的問題。在大多數情況下,計算的結果是準確的,但是多試幾次(可以做一個循環)就可以試出類似上面的錯誤。現在終于理解為什么要有BCD碼了。??
這個問題相當嚴重,如果你有9.999999999999元,你的計算機是不會認為你可以購買10元的商品的。??
在有的編程語言中提供了專門的貨幣類型來處理這種情況,但是Java沒有。現在讓我們看看如何解決這個問題。
四舍五入??
我們的第一個反應是做四舍五入。Math類中的round方法不能設置保留幾位小數,我們只能象這樣(保留兩位):??
非常不幸,上面的代碼并不能正常工作,給這個方法傳入4.015它將返回4.01而不是4.02,如我們在上面看到的??
4.015*100=401.49999999999994??
因此如果我們要做到精確的四舍五入,不能利用簡單類型做任何運算??
java.text.DecimalFormat也不能解決這個問題:
輸出是4.02??
BigDecimal??
在《Effective?? Java》這本書中也提到這個原則,float和double只能用來做科學計算或者是工程計算,在商業計算中我們要用java.math.BigDecimal。BigDecimal一共有4個夠造方法,我們不關心用BigInteger來夠造的那兩個,那么還有兩個,它們是:?
上面的API簡要描述相當的明確,而且通常情況下,上面的那一個使用起來要方便一些。我們可能想都不想就用上了,會有什么問題呢?等到出了問題的時候,才發現上面哪個夠造方法的詳細說明中有這么一段:??
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,
原來我們如果需要精確計算,非要用String來夠造BigDecimal不可!在《Effective?? Java》一書中的例子是用String來夠造BigDecimal的,但是書上卻沒有強調這一點,這也許是一個小小的失誤吧。?
解決方案??
現在我們已經可以解決這個問題了,原則是使用BigDecimal并且一定要用String來夠造。??
但是想像一下吧,如果我們要做一個加法運算,需要先將兩個浮點數轉為String,然后夠造成BigDecimal,在其中一個上調用add方法,傳入另一個作為參數,然后把運算的結果(BigDecimal)再轉換為浮點數。你能夠忍受這么煩瑣的過程嗎?下面我們提供一個工具類Arith來簡化操作。它提供以下靜態方法,包括加減乘除和四舍五入:??
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()
返回一個 BigDecimal ,其值是該數的絕對值,其標度是 this.scale() 。
add(BigDecimal)
返回一個 BigDecimal ,其值是 (this + val),其標度是 MAX(this.scale(),val.scale) 。
compareTo(BigDecimal)
返回 -1、0 或 1,分別表示該數是小于、等于、或大于 val 。
divide(BigDecimal, int)
返回一個 BigDecimal ,其值是 (this/val),其標度是 this.scale() 。
divide(BigDecimal, int, int)
返回一個 BigDecimal ,其值是 (this / val),其標度是指定值 。
doubleValue()
把一個數字轉換為 double 型。
equals(Object)
如果 x 是一個等于該數字的 BigDecimal ,則返回 true。
floatValue()
把該數字轉換為 float 型。
hashCode()
計算該對象的散列碼。
intValue()
把該數字轉換為 int 值。
longValue()
把該數字轉換為 long 型。
max(BigDecimal)
返回 BigDecimal ,其值是 this 和 val 中的較大者。
min(BigDecimal)
返回 BigDecimal ,其值是 this 和 val 中的較小者。
movePointLeft(int)
返回一個 BigDecimal ,其值等于該數十進制小數點向左移動 n 位后所得的值。
movePointRight(int)
把十進制小數點按指定數值向右移動相應位數。
multiply(BigDecimal)
返回一個 BigDecimal ,其值是 (this * val),其標度是 this.scale() + val.scale 。
negate()
返回一個 BigDecimal ,其值是 -1 * val ,其標度是 this.scale() 。
scale()
返回該數值的標度。
setScale(int)
返回一個 BigDecimal ,其標度是指定值,其數值精確等于該數字的值。
setScale(int, int)
返回一個 BigDecimal ,其標度是指定值,其整數值是該 BigDecimal 的整數部分被 10 的適當次冪(保持整個數值不變)乘或者除得到。
signum()
返回該數值的符號(即根據該數的值是正、零或負返回 -1 、 0 或 1 )。
subtract(BigDecimal)
返回一個 BigDecimal ,其值是 (this - val),其標度是 MAX(this.scale(),val.scale) 。
toBigInteger()
把該數字轉換為 BigInteger 。
toString()
返回表示該數字的字符串。
valueOf(long)
按照給定的值和零標度返回一個 BigDecimal 。
valueOf(long, int)
返回一個 BigDecimal ,其值是 (val/10**scale)。
變量
ROUND_UP
public static final int ROUND_UP
總是在非零的舍棄小數(即截斷)之前增加數字。 注意該舍入模式不減少量值。 (從零開始舍入)
ROUND_DOWN
public static final int ROUND_DOWN
從不在舍棄的小數(即截斷)之前增加數字。 注意該舍入模式不增加量值。 (舍入到零)
ROUND_CEILING
public static final int ROUND_CEILING
如果 BigDecimal 為正,則作 ROUND_UP 操作;如果為負,則作 ROUND_DOWN 操作。注意該舍入模式不減少值。(舍入到正無窮大)
ROUND_FLOOR
public static final int ROUND_FLOOR
如果 BigDecimal 為正,則作 ROUND_UP ;如果為負,則作 ROUND_DOWN 。注意該舍入模式不增加值。(舍入到負無窮大)
ROUND_HALF_UP
public static final int ROUND_HALF_UP
若舍棄部分>=.5,則作 ROUND_UP ;否則,則作 ROUND_DOWN (舍入到 “最近的數值”,除非向上舍入和向下舍入的距離是相等的)。
ROUND_HALF_DOWN
public static final int ROUND_HALF_DOWN
若舍棄部分> .5 ,則作 ROUND_DOWN;否則,作 ROUND_DOWN 操作(舍入到 “最近的數值”,除非向下舍入和向上舍入的距離相等)。
ROUND_HALF_EVEN
public static final int ROUND_HALF_EVEN
如果舍棄部分左邊的數字為奇數,則作 ROUND_HALF_UP 操作;如果它為偶數,則作 ROUND_HALF_DOWN 操作(舍入到 “最近的數值”,除非向到兩邊的距離相等)。
ROUND_UNNECESSARY
public static final int ROUND_UNNECESSARY
該 “偽舍入模式”實際是要求操作有一個精確結果,,因此不需要舍入。如果該舍入模式對一個指定的操作產生不精確的結果,則拋出算術異常。
?
?
?
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的java 数字计算精度问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微服务架构设计模式~根据业务能力进行服务
- 下一篇: PDF签名系列(1):PDF签名机制的漏