java基礎學習之十一:BigDecimal和BigInteger

趙明威發表於2016-06-07

借用《Effactive Java》這本書中的話,float和double型別的主要設計目標是為了科學計算和工程計算。他們執行二進位制浮點運算,這是為了在廣域數值範圍上提供較為精確的快速近似計算而精心設計的。然而,它們沒有提供完全精確的結果,所以不應該被用於要求精確結果的場合。float和double型別尤其不適合用於貨幣計算,商業計算往往要求結果精確,這時候BigDecimal和BigInteger就派上大用場啦。

一、基本資料型別的缺點

取出來java基礎學習之二:變數、基本資料型別 的圖

基本資料型別

float 和 double 不能進行很精確的計算 比如 金融資料計算, 根據下面一個例子可以看到精度是損失的,具體原因請參考 java float double精度為什麼會丟失?淺談java的浮點數精度問題 Effective Java 第 190頁 有一個例子:來說明浮點數精度的問題

題目描述:

貨架上有 10美分,20美分,30美分,等等 一直到1美元的糖果,現在你兜裡有1美元,從10美分開始 , 每種糖果買一顆,到不能支付位置,你可以買多少顆糖果,還會找多少零頭?

package com.test;
public class BigTest {
public static void main(String[] args) {
    boughtSugar();

 }

public static void boughtSugar(){
    double funds = 1.00;
    int itemsBought = 0;
    for (double price = .10; funds >= price; price += .10){
        funds -= price;
        itemsBought++;
    }
    System.out.println(itemsBought + " items bought");
    System.out.println("Change: $"+funds);
}
}

結果:

3 items bought
Change: $0.3999999999999999

同時 int , 和long 型別又不能滿足大數的計算,比如 求斐波那契數列的第100項的值,請參看演算法導論學習之補漏:斐波那契數列

那麼Java中就有了Big系列的解決辦法.

二、BigDecimal

Java在java.math包中提供的API 類 BigDecimal,超過16位有效位的數進行精確的運算。BigDecimal所建立的物件不能用傳統的+、-、*、/等算術運算子直接對其物件進行數學運算, 下面將說一下具體的建立物件和運演算法則。

1 構造器

BigDecimal(int) 建立一個具有引數所指定整數值的物件。

BigDecimal(double) 建立一個具有引數所指定雙精度值的物件。

BigDecimal(long) 建立一個具有引數所指定長整數值的物件。

BigDecimal(String) 建立一個具有引數所指定以字串表示的數值的物件。

2 方法描述

add(BigDecimal) BigDecimal物件中的值相加,然後返回這個物件。

subtract(BigDecimal) BigDecimal物件中的值相減,然後返回這個物件。

multiply(BigDecimal) BigDecimal物件中的值相乘,然後返回這個物件。

divide(BigDecimal) BigDecimal物件中的值相除,然後返回這個物件。

toString() 將BigDecimal物件的數值轉換成字串。

doubleValue() 將BigDecimal物件中的值以雙精度數返回。

floatValue() 將BigDecimal物件中的值以單精度數返回。

longValue() 將BigDecimal物件中的值以長整數返回。

intValue() 將BigDecimal物件中的值以整數返回。

4 比較大小

BigDecimal是通過使用compareTo(BigDecimal) 來比較 , 相等返回 0 , 大於 返回正值,小於 返回負值

5 用法示例

把上個題目進行改造成 BigDecimal的如下

   public static void boughtSugarBig(){

    final BigDecimal TEN_CENTS = new BigDecimal(".10"); //建立 10美分

    BigDecimal funds = new BigDecimal("1.00");
    int itemsBought = 0;
    for (BigDecimal price = TEN_CENTS; funds.compareTo(price) >= 0;
                   price =  price.add(TEN_CENTS)){
        funds = funds.subtract(price);
        itemsBought++;
    }
    System.out.println(itemsBought + " items bought");
    System.out.println("Change: $"+funds);
} 

結果:

4 items bought
Change: $0.00

6 在建立BigDecimal 的時候為什麼要用字串

最主要的原因是因為前面所說的 float 和 double 的精度會出現損失 , 例如:

Scanner s  = new Scanner(System.in);
String  r ;
double r1;
//輸入 r1 String型別, r2 double型別 
r = s.next();
r1 = s.nextDouble();

BigDecimal bigDecR = new BigDecimal(r);
BigDecimal bigDecR1 = new BigDecimal(r1);
//輸出
System.out.println(bigDecR+"\n"+bigDecR1);

輸入:

12.2
12.2

結果:

12.2
12.199999999999999289457264239899814128875732421875

double精度損失,有時候要進行冪的運算的時候會有更大的損失,所以在要求精確的計算時候, 不要使用 float 和double

三、BigInteger

BigInteger 能表示任意大的整數,原則上是,只要你的計算機的記憶體足夠大,可以有無限位的

演算法導論學習之補漏:斐波那契數列中 , int 和 long 都不能表示 第100項

1 構造器

BigInteger(byte[] val)將含有二進位制補碼二進位制表示BigInteger為BigInteger的位元組陣列。 BigInteger(int signum, byte[] magnitude)將BigInteger的符號數值表示法轉化為BigInteger。 BigInteger(int bitLength, int certainty, Random rnd) 建立一個隨機產生一個指定位數的正值BigInteger可能是素數。

BigInteger(int numBits, Random rnd) 構建了一個隨機生成的BigInteger,均勻地分佈在範圍0到(2numbits - 1)(包括)。

BigInteger(String val) 轉化BigInteger的十進位制字串為一個BigInteger。 BigInteger(String val, int radix)將在指定的基數BigInteger的字串表示形式為BigInteger。

2 方法描述

abs() 返回絕對值

add(BigInteger val) 加法 , 加上一個val 並返回 兩個數的和

divide(BigInteger val) 除法 , 除以 val ,返回結果

compareTo(BigInteger val) 比較大小

multiply(BigInteger val) 乘 , 乘以 val 返回結果

subtract(BigInteger val) 減法 減去 val 返回結果

四、細節

1 BigInteger與BigDecimal都是不可變的(immutable)的,在進行每一步運算時,都會產生一個新的物件,由於建立物件會引起開銷,因此它們不適合於大量的數學運算,應儘量使用long、float、double等基本型別做科學計算或者工程計算。設計BigInteger與BigDecimal的目的是用來精確地表示大整數和小數,常用於商業計算中。

2 BigDecimal夠造方法的引數型別有4種,其中的兩個用BigInteger構造,另一個是用double構造,還有一個使用String構造。應該避免使用double構造BigDecimal,因為:有些數字用double根本無法精確表示,傳給BigDecimal構造方法時就已經不精確了。

比如,new BigDecimal(0.1)得到的值是0.1000000000000000055511151231257827021181583404541015625。使用new BigDecimal("0.1")得到的值是0.1。因此,如果需要精確計算,用String構造BigDecimal,避免用double構造,儘管它看起來更簡單!

3 equals()方法認為0.1和0.1是相等的,返回true,而認為0.10和0.1是不等的,結果返回false。方法compareTo()則認為0.1與0.1相等,0.10與0.1也相等。所以在從數值上比較兩個BigDecimal值時,應該使用compareTo()而不是 equals()。

4 另外還有一些情形,任意精度的小數運算仍不能表示精確結果。例如,1除以9會產生無限迴圈的小數 .111111...。出於這個原因,在進行除法運算時,BigDecimal可以讓您顯式地控制舍入。


最近申請了微信公眾號,希望大家來看看,專門為程式設計師而生,做最好的程式設計

高斯程式設計

相關文章