java BigDecimal實現高精度數學函式計算
BigDecimal常常被用在我的計算器程式當中,因為它可以實現高精度計算(而且可以滿足我對某些數字的好奇心,比如我用它來計算圓周率,看看圓周率後幾萬位長什麼樣)。但是我發現好像BigDecimal並沒有提供sin,cos,log等函式的計算,也就是說我的計算器就不能把sin,cos計算到小數點後很多位去了。
剛開始時我很天真地用了這種方法來計算”高精度的”三角函式:
String result=BigDecimal.valueOf(Math.cos(a.doubleValue())).toPlainString();
輸了個數進去,發現結果是對的!但是這似乎不是高精度的,本質上還是雙精度浮點數的計算。於是為了滿足我對三角函式值的好奇心,我寫了一個用BigDecimal計算三角函式的類。後來因為強迫症,我又把其他的數學函式給加上去了。
目前我的BigMath類支援以下22個函式:
1.三角函式和反三角函式:sin,cos,tan,asin,acos,atan
2.冪及其逆運算:pow,sqrt,cbrt,root,log10,log,ln,exp
3.雙曲三角函式和反雙曲三角函式:sinh,cosh,tanh,asinh,acosh,atanh
4.弧度角度轉換:deg,rad
使用方法:
//用BigMath類的getDefaultBigMath方法獲取到BigMath物件,方法需要傳入一個PrecisionHolder
BigMath BM=BigMath.getDefaultBigMath(precision );
//上面的precision用於在計算過程中獲取計算所需要的精度
PrecisionHolder precision=new PrecisionHolder(){
@Override
public int getPrecision() {
return 50;//返回50,代表結果精確到50位
}
};
然後就可以呼叫BigMath裡的方法了
測試如下:
public class Test0 {
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in );
PrecisionHolder precision=new PrecisionHolder(){
@Override
public int getPrecision() {
System.err.println("請輸入精度");
int p=scanner.nextInt();
return p;
}
};
BigMath BM=BigMath.getDefaultBigMath(precision);
while(true){
System.err.println("請輸入函式名稱.");
System.err.println("支援的函式:sin,cos,tan,asin,acos,atan, pow,sqrt,cbrt,root, log10,log,ln,exp, sinh,cosh,tanh,asinh,acosh,atanh, deg,rad");
String s=scanner.next();
if(s.equals("exit")){
scanner.close();
System.exit(0);
}
if(s.equals("sin")){
System.out.println("結果:"+BM.sin(readDec1(scanner)));
}else if(s.equals("cos")){
System.out.println("結果:"+BM.cos(readDec1(scanner)));
}
//.........省略
else if(s.equals("atanh")){
System.out.println("結果:"+BM.atanh(readDec1(scanner)));
}else if(s.equals("rad")){
System.out.println("結果:"+BM.rad(readDec1(scanner)));
}
System.err.println("=======================================");
}
}
private static BigDecimal readDec1(Scanner scanner){
System.err.println("請輸入第一個變數");
return new BigDecimal(scanner.next());
}
private static BigDecimal readDec2(Scanner scanner){
System.err.println("請輸入第二個變數");
return new BigDecimal(scanner.next());
}
}
測試結果:
sin:(輸入為π/6,即60度角)
pow:(3.73456789的2.58次方)
root:(1234567898520開23.7次方)
sinh和asinh:(這裡的輸出順序有點問題,不過結果是正確的)
大部分函式是用泰勒展開或牛頓迭代法計算的,畢竟是BigDecimal,精度高時(大概小數點後100位)計算速度掉的厲害,不過我已經儘量優化減少計算次數來減小耗時了
好吧我承認這似乎沒什麼用,但既然都做出來了就分享一下吧
附:
由於java的Math類並沒有提供asinh,acosh和atanh方法,所以我還寫了一個類來補上這個空缺,jar裡有個JMath類,提供了這三個靜態方法,直接呼叫就可以了
double d=1.2345;
double result1=JMath.asinh(d);
double result2=JMath.acosh(d);
double result3=JMath.atanh(d);
jar下載地址:https://pan.baidu.com/s/1slbjdUX