1. 程式人生 > 其它 >使用Java實現的高精度科學計算器

使用Java實現的高精度科學計算器

技術標籤:計算器java演算法

高精度科學計算器

混合運算展示:

在這裡插入圖片描述

精度展示:

在這裡插入圖片描述

簡介:

可以進行混合運算,混合運算包括加法+,減法-,乘法*,除法/,取餘%,左括號(,右括號),根號√,對數log,指數^。

如:輸入:(3+2)^2+108/5-10log2+3√64

​ 輸出:47.27807190511263678372

理論可以達到無限精度,過高的精度會增加計算時間,需要提前設定精度,即小數點後多少位,預設為32位。

按鍵功能及使用方法:
按鍵功能使用方法
CE清空點選該鍵清空輸入輸出
清除點選該鍵清除最後一位輸入
Exp指數表示正在更新。。。
+/-符號切換正在更新。。。
+加法可用於混合運算,輸入“被加數”,“+”,“加數”後點擊“=”計算結果
-減法同加法
*乘法同加法
/除法同加法
Mod取餘同加法,按鍵顯示為“Mod”,運算子為”%“
(左括號用於混合運算,限制優先順序
)右括號用於混合運算,限制優先順序
Rad/Deg規定三角函式計算時的單位按鍵顯示Rad代表使用弧度單位,按鍵顯示Deg代表使用角度單位
2nd切換按鍵的第二功能點選該按鍵,三角函式切換為反三角函式,指數和對數功能按鍵也會進行轉換
sin計算正弦函式根據Rad/Deg按鍵輸入對應單位的引數,點選該鍵直接計算結果
cos計算餘弦函式根據Rad/Deg按鍵輸入對應單位的引數,點選該鍵直接計算結果
tan計算正切函式根據Rad/Deg按鍵輸入對應單位的引數,點選該鍵直接計算結果
asin計算反正弦函式輸入引數,點選該鍵根據Rad/Deg按鍵計算對應單位的結果
acos計算反餘弦函式輸入引數,點選該鍵根據Rad/Deg按鍵計算對應單位的結果
atan計算反正切函式輸入引數,點選該鍵根據Rad/Deg按鍵計算對應單位的結果
2√x平方根輸入引數,點選該按鍵直接計算結果
3√x立方根輸入引數,點選該按鍵直接計算結果
x^2平方輸入引數,點選該按鍵直接計算結果
x^3立方輸入引數,點選該按鍵直接計算結果
x^nn次方先輸入x,點選該鍵,再輸入n,點選“=”計算結果,可用於混合運算。
n√xn次根下x先輸入n,點選該鍵,再輸入x,點選“=”計算結果,可用於混合運算。
log計算以10為底的對數輸入引數,點選該按鍵直接計算結果。
ylogx以x為底y的對數先輸入y,點選該鍵,再輸入x,點選“=”計算結果,可用於混合運算。
ln計算以e為底的對數輸入引數,點選該按鍵直接計算結果。
e^xe的x次方輸入引數,點選該按鍵直接計算結果。
π圓周率點選該鍵顯示圓周率π的數值,該數值精度與設定精度有關。
e自然常數e點選該鍵顯示自然常數e的數值,該數值精度與設定精度有關。
1/x倒數輸入引數,點選該鍵直接計算結果。
運算結果精度說明:

程式中使用Java的BigDecimal類實現高精度,BigDecimal類中自帶加法add()、減法subtract()、乘法multiply()、除法**divide()及整數次方pow()**函式。

在上述函式基礎上自己實現對數函式log()、指數函式pow()、三角函式sin()cos()tan()arcsin()arccos()arctan()、**arccot()**函式,同樣可以達到無限精度。

步驟:

  • 首先實現階乘函式

    public BigDecimal fac(int n)
    
  • ln(x)的泰勒展開為:
    ln ⁡ ( x ) = ln ⁡ ( 1 + y 1 − y ) = 2 y ( 1 1 + 1 3 y 2 + 1 5 y 4 + 1 7 y 6 + 1 9 y 8 + ⋯   ) \ln (x)=\ln \left(\frac{1+y}{1-y}\right) \quad=2 y\left(\frac{1}{1}+\frac{1}{3} y^{2}+\frac{1}{5} y^{4}+\frac{1}{7} y^{6}+\frac{1}{9} y^{8}+\cdots\right) ln(x)=ln(1y1+y)=2y(11+31y2+51y4+71y6+91y8+)
    其中y = (x - 1) / (x + 1)

    在迴圈中不斷累加,如果當前項的值小於設定精度,預設小於10^-32時退出。

    但該展開式只有在x接近1時收斂比較快,如果計算ln(10)經測試大概耗時3s,完全不能忍。

    接下來通過放縮區間加快速度,已知ln(xy)=ln(x)+ln(y),找一個折中的辦法,先將引數x 10倍放縮到[0.5, 5]區間,標記縮放次數為ln10Count,再將引數x 1.1倍縮放到[0.95, 1.05]區間得到x’,標記縮放次數為ln1_1Count,最終結果為ln10Count*ln(10) + ln1_1Count*ln(1.1) + ln(x’),該方法需要提前計算ln(10)與ln(1.1),但計算一次可以永久使用,對於ln(10)轉化為10*ln(1.25) + ln(1.073741824)進一步加快速度。

  • y log x即以x為底y的對數可轉換為log(y)/log(x)

  • a^x的泰勒展開式為:
    a x = e x ln ⁡ a = 1 + x ln ⁡ a 1 ! + ( x ln ⁡ a ) 2 2 ! + ( x ln ⁡ a ) 3 3 ! + ⋯ a^{x}=e^{x \ln a}=1+\frac{x \ln a}{1 !}+\frac{(x \ln a)^{2}}{2 !}+\frac{(x \ln a)^{3}}{3 !}+\cdots ax=exlna=1+1!xlna+2!(xlna)2+3!(xlna)3+
    其中需要前面實現的ln(x)

  • sin(x)的泰勒展開式為:
    sin ⁡ x = ∑ n = 0 ∞ ( − 1 ) n ( 2 n + 1 ) ! x 2 n + 1 = x − 1 3 ! x 3 + 1 5 ! x 5 − ⋯ + ( − 1 ) n ( 2 n + 1 ) ! x 2 n + 1 + ⋯ \sin x=\sum_{n=0}^{\infty} \frac{(-1)^{n}}{(2 n+1) !} x^{2 n+1}=x-\frac{1}{3 !} x^{3}+\frac{1}{5 !} x^{5}-\cdots+\frac{(-1)^{n}}{(2 n+1) !} x^{2 n+1}+\cdots sinx=n=0(2n+1)!(1)nx2n+1=x3!1x3+5!1x5+(2n+1)!(1)nx2n+1+
    該展開式只能計算[0, PI/2]區間的sin(x)的值,需要先將引數轉換到此區間

  • cos(x)同理

  • tan(x) = sin(x)/cos(x)

  • arctan(x)的泰勒展開式為:
    arctan ⁡ x = ∑ n = 0 ∞ ( − 1 ) n 2 n + 1 x 2 n + 1 = x − 1 3 x 3 + 1 5 x 5 + ⋯ + ( − 1 ) n 2 n + 1 x 2 n + 1 + ⋯ \arctan x=\sum_{n=0}^{\infty} \frac{(-1)^{n}}{2 n+1} x^{2 n+1}=x-\frac{1}{3} x^{3}+\frac{1}{5} x^{5}+\cdots+\frac{(-1)^{n}}{2 n+1} x^{2 n+1}+\cdots arctanx=n=02n+1(1)nx2n+1=x31x3+51x5++2n+1(1)nx2n+1+
    x定義域為實數集,但|x|在接近1或大於1的時候收斂速度特別慢

    解決辦法:

    1. 使用arctan(-x) = -arctan(x),將引數限制到正實數
    2. 如果x>1,根據arctan(x)=PI/2-arctan(1/x),求arctan(1/x),將引數轉換到[0, 1]區間
    3. 但x在接近1時(如0.99)收斂仍然很慢,接下來將引數x限制到[0, 0.5]區間
    4. 如果x>0.5,根據arctan(x)=arctan(y)+arctan((x-y)/(1+xy)),將arctan的引數限制到0.5以下,此處可以選更小的值達到更快的速度
  • arccot(x) = PI/2 - arctan(x)

  • arcsin(x) = arctan(x/sqrt(1-x^2))

  • arccos(x) = PI/2 - arcsin(x)

混合運算實現流程:
  • 使用正則表示式提取輸入算式的引數和運算子

    用於匹配的正則表示式:
    在這裡插入圖片描述

  • 將中綴表示式轉為字尾表示式

  • 對字尾表示式進行計算

更新日誌:
  • 2020.12.8 20.18

    三角函式仍使用double進行計算,精確到小數點後(16-整數位數)位(如計算atan(80°)=89.28384005452959,即小數位數為16-2位整數位=14位小數位),相當於對double變量表示不了的精度取了個近似值,計算結果不會再出現sin(30°) =0.49999999999999994這種情況。

  • 2020.12.10 16.12

    正則表示式進行了優化,加入正後顧可以正確匹配負數和減法,比如對於“2-3”匹配結果應該為(“2”,“-”,“3”),對於“-2–3”匹配結果應該為(“-2”,“-”,“-3”)。

  • 2020.12.10 16.12

    對數函式仍使用double進行計算,使用與三角函式相同的方法解決精度問題。

  • 2020.12.11 10.46

    結果使用DecimalFormat.format()格式化字串,三位一體使用“,”分開顯示,但輸入引數還沒有實現,如果要實現的話必須要跟win10計算器一樣當引數輸入完畢,輸入運算子時引數才上移。

  • 2020.12.14 19.07

    使用BigDecimal實現常用數學函式,可以達到無限精度

    輸出結果三位一體分隔開,便於閱讀,輸入暫未實現

待優化功能:
  • 將輸入引數和計算結果三位一體使用“,”分隔開,便於閱讀,可以使用DecimalFormat.format()函式。
  • 三角函式應該有角度制、弧度制、百分度制(梯度制),暫未實現百分度制。
  • 使用BigDecimal實現對數函式及指數函式求解(log,ln,√,^),這樣可以達到任意精度,不再侷限於double的精度。
  • 計算前檢查算式的合法性,直接用規則檢查比較複雜,可以在混合運算解析的過程中使用try和catch捕獲異常,對其他非混合運算算式可直接檢查。比如檢查反三角函式的輸入引數範圍可以直接用規則檢查,檢查階乘的引數是不是整數二者都可以。

現對數函式及指數函式求解(log,ln,√,^),這樣可以達到任意精度,不再侷限於double的精度。~~

  • 計算前檢查算式的合法性,直接用規則檢查比較複雜,可以在混合運算解析的過程中使用try和catch捕獲異常,對其他非混合運算算式可直接檢查。比如檢查反三角函式的輸入引數範圍可以直接用規則檢查,檢查階乘的引數是不是整數二者都可以。

這個計算器也是匆匆趕時間完成的,再加上不可能考慮到所有輸入情況,沒有對錯誤的輸入進行提示,其中肯定存在不少問題,專案地址在下面,其中包括4個java檔案,CalculatorHMI實現計算器的圖形介面及按鍵的事件處理,修改其中的accuracy變數可以改變精度,也就是結果精確到小數點後多少位,預設為32位,更高的位數也可以,但計算時間也會加長。BigDecimalMath就是用泰勒公式重寫的Math庫中的函式,MixedOperation實現混合運算,Main檔案中就是主函式,是整個工程的入口。當然電腦上必須有jre也就是java執行環境才可以執行。

專案地址:https://github.com/STM32xxx/JavaCalculator

------故里草木深------