功能特性
- 支援四則混合運算(+、-、*、/)
- 支援括號巢狀:()、[]、{} 自動統一
- 支援
int() 取整與 round() 四捨五入函數
- 支援負數運算元(如
*-3、+-5)
- 錯誤檢測:除零、運算元缺失、括號不匹配、函數語法錯誤
核心演算法邏輯
- 預處理:移除空格、統一括號、小寫轉換
- 遞歸處理括號:由內而外計算最內層括號內表達式
- 處理 round 函數:解析
round(值, 位數),以 floor(x * 10^n + 0.5) / 10^n 實現
- 處理 int 函數:直接
floor(值)
- 處理乘除序列:左結合,支援負數運算元,透過
IsMinus 標記處理連續符號
- 處理加減序列:最後處理剩餘加減,同樣支援負數
完整原始碼
#include <math.h>
#pragma warn -8060
AnsiString __fastcall Calc(String sExp)
{
int posL, pos, posR, posM;
String sTmp, sL, sR;
bool IsMinus;
sExp = sExp.LowerCase();
if (sExp.AnsiPos("error")) return sExp;
while (pos = sExp.AnsiPos(" ")) sExp = sExp.Delete(pos, 1);
if (sExp.IsEmpty()) return "0";
// 統一括號
while ((pos = sExp.AnsiPos("[")) > 0 || (pos = sExp.AnsiPos("{")) > 0)
sExp = sExp.SubString(1, pos-1) + "(" + sExp.SubString(pos+1, sExp.Length());
while ((pos = sExp.AnsiPos("]")) > 0 || (pos = sExp.AnsiPos("}")) > 0)
sExp = sExp.SubString(1, pos-1) + ")" + sExp.SubString(pos+1, sExp.Length());
// 處理 round()
while (pos = sExp.AnsiPos("round(")) {
posL = pos + 5;
while (posM = sExp.SubString(posL+1, sExp.Length()).AnsiPos("round("))
posL = posL + posM + 5;
for (posM = posL+2; !sExp.IsDelimiter(",", posM) && posM <= sExp.Length(); posM++);
if (posM == 0) return "error:round沒有配對的逗號, 公式錯!";
for (posR = posM+2; !sExp.IsDelimiter(")", posR) && posR <= sExp.Length(); posR++);
sExp = sExp.SubString(1, posL-6)
+ floor(Calc(sExp.SubString(posL+1, posM-posL-1)).ToDouble() *
pow(10, sExp.SubString(posM+1, posR-posM-1).ToDouble()) + 0.5)
/ pow(10, sExp.SubString(posM+1, posR-posM-1).ToDouble())
+ sExp.SubString(posR+1, sExp.Length());
}
// 遞歸處理括號
while (posL = sExp.LastDelimiter("(")) {
sTmp = sExp.SubString(posL+1, sExp.Length());
posR = sTmp.AnsiPos(")");
if (posR == 0) return "error:沒有配對的), 公式錯!";
sExp = sExp.SubString(1, posL-1) + Calc(sTmp.SubString(1, posR-1)) + sTmp.SubString(posR+1, sTmp.Length());
}
// 處理 int()
while (pos = sExp.AnsiPos("int")) {
posL = pos + 2;
for (posR = posL+2; !sExp.IsDelimiter("+-*/", posR) && posR <= sExp.Length(); posR++);
sExp = sExp.SubString(1, pos-1) + floor(sExp.SubString(posL+1, posR-posL-1).ToDouble()) + sExp.SubString(posR, sExp.Length());
}
// 處理 */ 序列
IsMinus = false;
while (sExp.LastDelimiter("*/")) {
for (pos = 1; !sExp.IsDelimiter("*/", pos) && pos <= sExp.Length(); pos++);
if (pos == 1 || pos == sExp.Length()) return "error:首或尾字符是*/運算符, 公式錯!";
posL = sExp.SubString(1, pos-1).LastDelimiter("+-");
Minus0:
for (posR = pos+1; !sExp.IsDelimiter("+-*/", posR) && posR <= sExp.Length(); posR++);
if (posR == (pos+1) && sExp.IsDelimiter("*/", posR)) return "error:*/運算符相鄰, 公式錯!";
if (posR == sExp.Length()) return "error:尾字符是+-*/運算符, 公式錯!";
if (sExp.SubString(pos, 2) == "*-" || sExp.SubString(pos, 2) == "/-") {
sExp.Delete(pos+1, 1); IsMinus = !IsMinus; goto Minus0;
}
sL = sExp.SubString(posL+1, pos-posL-1);
sR = sExp.SubString(pos+1, posR-pos-1);
if (sExp.IsDelimiter("/", pos) && sR == "0") return "error:除數為零,無意義!";
sExp = (posL == 0 ? "" : sExp.SubString(1, posL))
+ (sExp.IsDelimiter("*", pos) ? (sL.ToDouble()*sR.ToDouble()) : (sL.ToDouble()/sR.ToDouble()))
+ sExp.SubString(posR, sExp.Length());
}
if (IsMinus) sExp = "-" + sExp;
// 處理 +- 序列
IsMinus = false;
while (sExp.LastDelimiter("+-")) {
for (pos = 2; !sExp.IsDelimiter("+-", pos) && pos <= sExp.Length(); pos++);
if (pos == sExp.Length()) return "error:尾字符是+-運算符,公式錯!";
if (pos > sExp.Length()) return sExp;
Minus1:
for (posR = pos+1; !sExp.IsDelimiter("+-", posR) && posR <= sExp.Length(); posR++);
if (posR == sExp.Length()) return "error:尾字符是+-運算符,公式錯!";
if (sExp.SubString(pos, 2) == "+-" || sExp.SubString(pos, 2) == "--") {
sExp.Delete(pos+1, 1); IsMinus = !IsMinus; goto Minus1;
}
sL = sExp.SubString(1, pos-1);
sR = sExp.SubString(pos+1, posR-pos-1);
sExp = String(sExp.IsDelimiter("+", pos) ?
(sL.ToDouble() + sR.ToDouble() * (IsMinus ? -1 : 1)) :
(sL.ToDouble() - sR.ToDouble() * (IsMinus ? -1 : 1)))
+ sExp.SubString(posR, sExp.Length());
}
return sExp;
}
使用範例
Calc("3 + 5 * 2") // 13
Calc("round(3.14159, 2)") // 3.14
Calc("int(-3.7)") // -4
Calc("(-3) * (-4) + 5") // 17
Calc("2 * -3 + 4") // -2