發文作者:Blake Chang | 2021 年 02 月 23 日

FEB/23/2021 寫程式紀錄:Calcl v1.2.2.2 & Calcl MDI v1.0.6.2 任意精度計算工具

Calcl 是我開發的任意精度計算工具 (下載連結置於此文下方),開發的核心是終端機輸入版本;而 MDI 版本是設計給 Windows 圖形介面,MDI 版本可依使用者需求來選擇視窗:(1) 文字編輯器模式的計算機畫面,或 (2) Ctrl-W 切換成按鈕型計算機視窗,或 (3) 按鈕型計算機再 Ctrl-UP 可縮小成精簡模式的小型「懸浮」輸入框;Calcl 適合常常需要在 Windows 電腦螢幕計算數字的人。

這次我花了很多日子改寫,試著讓 Calcl 遇到超過使用者設定位數的運算時還能順利計算出來,但由於改寫很多處,也許還有尚未發現的 bug 或待改進的運算效能,若有需要上一個釋出版本,請參:JAN/17/2021 寫程式紀錄:Calcl v1.2.2.0 & Calcl MDI v1.0.6.0

關於 Calcl 運算位數的自動擴展研究,我除了建立整數位數的內部自動擴展功能,也對小數位數同時寫了相關擴展用的程式碼,以期運算結果於必要時仍能保留小數部分的最小有效位數,由於現實世界裡計算出的小數可能有無窮位數,此情況下不可能全部列出,所以小數位數最終有效長度依照使用者設定 (使用指令 set.digit) 反而是比較合適的方式,對於大部分運算結果都會顯示符合使用者設定的小數位數範圍,但若遇到運算結果超過設定的小數範圍時則會保留最小部分,比方小於 1 大於 0 的很小數字,類似 1/10^11111 = 0.00000000……00010 這種數字。

Calcl 核心版本 V1.2.2.2 釋出,更新紀錄:

● 在 L1calc.cpp 的 calclusrvar(…) 最後增加 if() 敘述:若 s0 原為 “{~}" or “{:c}" 清空變數,其經 calclusrvar(…) 處理到最後會變成 “.+",則強制改其為 “1″,避免程式當掉錯誤
● 在 calclKNL.cpp 的 calcl(…) 最下面進入 calcfuncBASEPROC(…) 之前的 if() 加入 || (err > 0 && (overflow_n > 0 || overflow_m > 0)) 條件,讓變數功能用 toolfuncs(…) 正常
● 在 Lcalc_base.cpp 增加全域變數 INT64 backup_INPUTMAX,用來備份 INPUTMAX,backup_INPUTMAX 用在 L1calc.cpp 的 calclusrvar(…) 及 calclKNL.cpp 的額外函式 AddTool_FBNCS(…) 備份 INPUTMAX,並在 calclKNL.cpp 的 calclKNL(…) 最上面復原預設的 INPUTMAX
● 對 calclKNL.cpp 的 calclKNL(…) 移除最上面起始 Str 的 for 迴圈程式碼,對 refreshEnv() 的 Str 大小改為 INPUTMAX + 5
● 對 calclKNL.cpp 額外函式 AddTool_FBNCS(…) 除錯:修改最後部分程式碼,計算分割率時偵測 overflow_n 與 overflow_m 發生、依需要動態擴增 Str[]、使用 size_t st = fread(Str, 1, i, f_FBNCS) 再令 Str[st] = ‘\0’
● 對 Lcalc_base.cpp 的 INT32 Lquotient(…) 除錯:最後 10^oT 調整的程式碼,增加 else Ans[p] = 0
● 對 Lcalc_base.cpp 的 Ladjust_rad(…) 加入 overflow_m 擴大機制(同步修改 Lrad_to_deg(…)、Lcos_rad(…)、Lsin_rad(…)):當整數超大時需提升小數精度至少 (提高 m),因 rad 的 pi 有無限小數位數,避免計算出超大整數倍數的 2pi 後卻因誤差超級擴大而計算失準
● 對 Lcalc_base.cpp 的 Lsqrt(…) 加入 overflow_n 與 overflow_m 擴大檢查的程式碼
● 在 L1calc.cpp 的 toolfuncs(…) 開始,若 tmp_LP_for_overflow == -1 時,主動擴展 toolfuncs 小數精度,使用 overflow_m = m + (m – LP_preci) 重新導入計算,相當於 LP_preci 減少 (m – LP_preci)
● 對 L1calc.cpp 的 freadCST(…)、fwriteCST(…) 移除開頭的限制:if(overflow_n > 0 || overflow_m > 0 || tmp_n_for_overflow != -1 || tmp_m_for_overflow != -1) return false; 和 if(!useCST) return false;
● 在 L1calc.cpp 的 calcfuncBASEPROC(…) 的 ^= 與 /= 程式碼區塊,對答案為 0 時,加入檢查 加入 || overflow_n > 0 || overflow_m > 0 條件
● 對 calclKNL.cpp 額外函式 AddTool_derivative(…) 持續優化:透過調整指派的 overflow_m
● 對 calclKNL.cpp 額外函式 AddTool_derivative(…) 調整一些程式碼,試著增加運作穩定度 (因為有發生記憶體錯誤狀況)
● 在 L1calc.cpp 的 calcfuncBASEPROC(…) toolfuncs() 區塊前加入 if(tmp_LP_for_overflow > 0) LP_preci = tmp_LP_for_overflow – 4,擴增精度,測試 tan(3pi/2) 應為無定義
● 對 Lcalc_base.cpp 的 Lsin_rad(…)、Lcos_rad(…):加入 if(tmp_LP_for_overflow > 0) LP_preci = tmp_LP_for_overflow – 2,測試 tan(3pi/2) 應為無定義
● 對 Lcalc_base.cpp 的 LModLastAns(…) 精度未被程式內部擴張過,增加 if(i <= LP_UpCheck) –i,保留小數非 0 的最大位數之值
● 對 calclKNL.cpp 額外函式 AddTool_mod(…) 除錯:以 2021/2/7 Google 計算機 mod 的運算結果來製作 mod 程式碼 (可處理正負數字),第二以
 https://www.omnicalculator.com/math/modulo 的計算說明製作程式碼:含有正的餘數、負的餘數
● Lcalc_base.cpp 修改 LModLastAns(INT32 *Ans):針對有整數且其小數位數已被程式內部擴張過的,且為小數第一個非 0 小於設定精度,做進位和捨去的調整
● Lcalc_base.cpp 修改 Lquotient(…)、Lproduct(…)、Lsin_rad(…)、Lcos_rad(…):最後部分都加入新程式碼,調整答案的小數在小於設定精度的部分做進位或捨去
● calclKNL.cpp 修改 INT32 StrCalclKNL::AddTool_RSA(…):加入 RSA_Value[n] = 0,令 RSA 值都為正值
● L1calc.cpp 修改 calcfuncBASEPROC(…):增加 bool _toolfuncs = 0,提供判定 digUpDecimalFractionForLmode,如果 _toolfuncs == 1 則不使用 digUpDecimalFractionForLmode,避免 overflow_m 擴增導致迴圈無限循環
● L1calc.cpp 在 calcfuncBASEPROC(…) 裡每個 smod() 前使用 digUpDecimalFractionForLmode = 1:強制 Lmode() 找出取得小數部分,對小數有效位數精度內為 0 但實則非 0 的可使用
● L1calc.cpp 對 adjustinputstr(char *s) 除錯:(1) 字串尾留有運算符號 +、-、*、/、^ 是錯誤 15,(2) 字串頭留有運算符號 ^ 是錯誤 15
● Lcalc_base.cpp 增加全域變數 static bool digUpDecimalFractionForLmode:用在 Lmode() 強制找出取得小數部分,對小數有效位數精度內為 0 但實則非 0 的可使用
● 對 L1calc.cpp 裡面 INT32 adjustinputstr(char *s) 除錯:(1) 字串尾留有運算符號 +、-、*、/、^ 是錯誤 15,(2) 字串頭留有運算符號 ^ 是錯誤 15
● 調整 L1calc.cpp 裡面 INT32 calcfuncBASEPROC(…) 的乘方 A^= B 與乘除法 A *= B、A /= B:若 A 非 0,但運算後 A 卻變為 0 (通常發生在除法,被除數比除數大非常多時),這樣會失去最小值變成 0,答案不對,故需進行 overflow_m 小數擴增,每次擴增 m,用來保留最小值維持精度,以利下一個運算符號計算
● 持續調整新版 INT32 Lquotient(…)。修改 bool LModLastAns(INT32 *Ans):針對小數位數已被程式內部擴張過的做進位和捨去的調整
● 額外函式加入使用 Lcalc_base.cpp 的 diviedByZero,可偵測除數有 0 的情況,避免進入運算後導致程式記憶體出錯
● 修理額外函式自動擴張 m 值的錯誤:必需維持和使用 tmp_m_for_overflow 傳遞正確訊息給 INT32 Lquotient(…),不可提早使用 resetOverflowMark(FALSE)
● 對額外函式 AddTool_derivative(…) 第三參數函數 f(x),使用者未填入變數 x 的情況進行偵測,避免進入運算後導致程式記憶體出錯

下載連結(包含核心版本 Calcl v1.2.2.2 for Windows+Linux+Android,及 Windows 圖形介面版本 Calcl MDI v1.0.6.2 with calclKNL 1.2.2.2)。

Note:安卓版使用 apk 安裝「升級」時,安裝新版本後,請先手動清除資料(第一次安裝的不需要),清除資料方式大致如下 (各牌手機可能略有不同):1. 設定 → 應用程式 → Calcl → 儲存位置 → 清除資料。2. 移除 /storage/emulated/0/calclmean。3. 打開 Calcl 程式即可開始使用。

此處為 calcl v1.2.2.2 安卓版畫面

發表留言

分類