Java浮點數運算的精確度和四捨五入的問題

NO IMAGE

浮點運算有時是不精確的,只要是超過精度能表示的範圍就會產生誤差。往往產生誤差不是因為數的大小,而是因為數的精度,產生的結果接近但不等於想要的結果,所以在使用 float 和 double 作精確運算的時候往往採用一些方案來實現運算的準確。

Java中宣告的小數預設是double型別的。double d=2.1;如果宣告float f=2.1;則會報錯,而應該寫為float f=2.1f;或float f=(float)2.1;float 記憶體分配4個位元組,佔32位,double型記憶體分配8個位元組,佔64位。

1.float和double型別轉換

兩種型別可以互相轉換,如兩種型別資料求和,float型別可以直接轉換為double,因為Java在運算時會自動的提升變數的精度來進行運算,double比float精度更高,所以可以自動的從float轉化至double再進行運算;而double不可直接轉換為float,同樣必須先強制轉換。但是轉換後計算結果可能出現錯誤。因為float到double需要補位,直接轉換會設計到精度問題,所以需要藉助字串,保證不丟失資料。如:

float f=2.1f;
double d = Double.parseDouble(String.valueOf(f));

或用

float f=2.1f;
BigDecimal bd = new BigDecimal(String.valueOf(f));
double d = bd.doubleValue();

2.運算時的不準確

在小數運算時也會出現這樣的不準確現象,此時需要使用BigDecimal進行精確的小數運算,將浮點數轉為String。BigDecimal是Java提供的一個不可變的、任意精度的有符號十進位制數。

如計算3.564與5.13的和,需要用String引數的建構函式來構造BigDecimal物件,不能使用double型別引數的建構函式,否則沒有效果。

double d1=3.564;
double d2=5.13;
BigDecimal b1 = new BigDecimal(Double.toString(d1)); 
BigDecimal b2 = new BigDecimal(Double.toString(d2));
System.out.println(b1.add(b2));

在這個類上定義了很多進行加減乘除運算的方法:add()、subtract()、multiply()、divide(),另外還有進行小數點移位運算的movePointLeft()和movePointRight()等。

3.四捨五入的問題:

在運算時,需要對計算結果四捨五入,有如下幾種方式可選用。

(1)使用BigDecimal

對結果進行四捨五入時,scale引數指定精度,RoundingMode是舍入模式,它是列舉型別。

BigDecimal b = new BigDecimal(Double.toString(v));
return b.setScale(scale, RoundingMode).doubleValue();

在除法運算中,當發生除不盡的情況時,可由scale引數指定精度,之後的數字四捨五入

BigDecimal b1 = new BigDecimal(Double.toString(v1));  
BigDecimal b2 = new BigDecimal(Double.toString(v2));  
return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue(); 

(2)使用DecimalFormat

DecimalFormat對數值格式化時可以進行四捨五入,但不是一般認識上的四捨五入,在DecimalFormat的API中如是介紹:DecimalFormat 提供 RoundingMode 中定義的舍入模式進行格式化。預設情況下,它使用 RoundingMode.HALF_EVEN。例如:

DecimalFormat decFormat = new DecimalFormat(“0.00”);
System.out.println(decFormat.format(sim));

如用以上程式碼進行格式化,則3.456~3.46;3.454~3.45;3.455~3.46;3.465~3.46(捨棄部分左邊的數字為偶數),可以修改RoundingMode,如新增設定decFormat.setRoundingMode(RoundingMode.HALF_UP);即為通常的四捨五入模式。

(3)使用格式控制

double d = 3.1415926;
String result = String .format(“%.2f”,d);

其中%.表示小數點前任意位數,2表示兩位小數,格式後的結果為f表示浮點型。

此外,還有Math.round()方式,但它並不能正確的滿足條件,它返回最接近引數的long,結果將舍入為整數。

如有不正確之處,歡迎指正指教~