java中如何進行高精度運算

import java.math.BigDecimal;
import java.math.BigInteger;

/**

* 爲了解決Java基本數據類型在運算時會出現的溢出和計算不精確的問題。
* Java 提供了兩個類BigInteger和BigDecimal,專門用於進行高精度運算
* 凡是能用int 或float 做的事情,用BigInteger和BigDecimal也可以做,
* 只是必須換用方法調用,而不是使用運算符。
*
* 高精度整數BigInteger
* BigInteger支持任意精度的整數,也就是說我們可精確表示任意大小的整數值;同時在運算過程中不會丟失任何信息;
*
* 高精度浮點數BigDecimal
* 它可以表示任意精度的小數,並對它們進行計算。
* 由於 BigDecimal 對象是不可變的,這些方法中的每一個都會產生新的 BigDecimal 對象。
* 因此,因爲創建對象的開銷,BigDecimal 不適合於大量的數學計算,但設計它的目的是用來精確地表示小數。
*/
public class BigNumber {

//默認除法運算精度,即保留小數點多少位
private static final int DEFAULT_DIV_SCALE = 10;

//這個類不能實例化
private BigNumber() {
}

/**
* 提供精確的加法運算。
* @param v1 被加數
* @param v2 加數
* @return 兩個參數的和
*/
public static double add(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return (b1.add(b2)).doubleValue();
}

/**
* 提供精確的減法運算。
* @param v1 被減數
* @param v2 減數
* @return 兩個參數的差
*/
public static double sub(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return (b1.subtract(b2)).doubleValue();
}

/**
* 提供精確的乘法運算。
* @param v1 被乘數
* @param v2 乘數
* @return 兩個參數的積
*/
public static double mul(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return (b1.multiply(b2)).doubleValue();
}

/**
* 提供(相對)精確的除法運算,當發生除不盡的情況時,精確到
* 小數點以後多少位,以後的數字四捨五入。
* @param v1 被除數
* @param v2 除數
* @return 兩個參數的商
*/
public static double div(double v1, double v2) {
return div(v1, v2, DEFAULT_DIV_SCALE);
}

/**
* 提供(相對)精確的除法運算。當發生除不盡的情況時,由scale參數指
* 定精度,以後的數字四捨五入。
* @param v1 被除數
* @param v2 除數
* @param scale 表示需要精確到小數點以後幾位。
* @return 兩個參數的商
*/
public static double div(double v1, double v2, int scale) {
if (scale < 0) {
System.err.println("除法精度必須大於0!");
return 0;
}
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return (b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP)).doubleValue();
}

/**
* 計算Factorial階乘!
* @param n 任意大於等於0的int
* @return n!的值
*/
public static BigInteger getFactorial(int n) {
if (n < 0) {
System.err.println("n必須大於等於0!");
return new BigInteger("-1");
} else if (n == 0) {
return new BigInteger("0");
}
//將數組換成字符串後構造BigInteger
BigInteger result = new BigInteger("1");
for (; n > 0; n--) {
//將數字n轉換成字符串後,再構造一個BigInteger對象,與現有結果做乘法
result = result.multiply(new BigInteger(new Integer(n).toString()));
}
return result;
}

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

//計算階乘,可以將n設得更大
int n = 30;
System.out.println("計算n的階乘" + n + "! = " + BigNumber.getFactorial(n));

//用double構造BigDecimal
BigDecimal bd1 = new BigDecimal(0.1);
System.out.println("(bd1 = new BigDecimal(0.1)) = " + bd1.toString());
//用String構造BigDecimal
BigDecimal bd2 = new BigDecimal("0.1");
System.out.println("(bd2 = new BigDecimal(\"0.1\")) = "
+ bd2.toString());

BigDecimal bd3 = new BigDecimal("0.10");
//equals方法比較兩個BigDecimal對象是否相等,相等返回true,不等返回false
System.out.println("bd2.equals(bd3) = " + bd2.equals(bd3));//false
//compareTo方法比較兩個BigDecimal對象的大小,相等返回0,小於返回-1,大於返回1。
System.out.println("bd2.compareTo(bd3) = " + bd2.compareTo(bd3));//0

//進行精確計算
System.out.println("0.05 + 0.01 = " + BigNumber.add(0.05, 0.01));
System.out.println("1.0 - 0.42 = " + BigNumber.add(1.0, 0.42));
System.out.println("4.015 * 100 =" + BigNumber.add(4.015, 100));
System.out.println("123.3 / 100 = " + BigNumber.add(123.3, 100));

/**
* (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可以讓您顯式地控制舍入。
*/

}

}

運行結果:

0.060000000000000005
0.5800000000000001
401.49999999999994
1.2329999999999999
計算n的階乘30! = 265252859812191058636308480000000
(bd1 = new BigDecimal(0.1)) = 0.1000000000000000055511151231257827021181583404541015625
(bd2 = new BigDecimal("0.1")) = 0.1
bd2.equals(bd3) = false
bd2.compareTo(bd3) = 0
0.05 + 0.01 = 0.06
1.0 - 0.42 = 1.42
4.015 * 100 =104.015
123.3 / 100 = 223.3
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章