使用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^n | n次方 | 先輸入x,點選該鍵,再輸入n,點選“=”計算結果,可用於混合運算。 |
n√x | n次根下x | 先輸入n,點選該鍵,再輸入x,點選“=”計算結果,可用於混合運算。 |
log | 計算以10為底的對數 | 輸入引數,點選該按鍵直接計算結果。 |
ylogx | 以x為底y的對數 | 先輸入y,點選該鍵,再輸入x,點選“=”計算結果,可用於混合運算。 |
ln | 計算以e為底的對數 | 輸入引數,點選該按鍵直接計算結果。 |
e^x | e的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(1−y1+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=x−3!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=0∑∞2n+1(−1)nx2n+1=x−31x3+51x5+⋯+2n+1(−1)nx2n+1+⋯
x定義域為實數集,但|x|在接近1或大於1的時候收斂速度特別慢解決辦法:
- 使用arctan(-x) = -arctan(x),將引數限制到正實數
- 如果x>1,根據arctan(x)=PI/2-arctan(1/x),求arctan(1/x),將引數轉換到[0, 1]區間
- 但x在接近1時(如0.99)收斂仍然很慢,接下來將引數x限制到[0, 0.5]區間
- 如果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
------故里草木深------