1. 程式人生 > >java BigDecimal實現高精度數學函式計算

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