數(shù)計(jì)算器實(shí)現(xiàn):面向?qū)ο笤O(shè)計(jì)、運(yùn)算符重載與工程實(shí)踐)
1. 項(xiàng)目緣起為什么我們需要一個(gè)“復(fù)雜”的復(fù)數(shù)計(jì)算器在工程、物理和信號(hào)處理領(lǐng)域復(fù)數(shù)計(jì)算是家常便飯。無(wú)論是分析交流電路的阻抗還是處理傅里葉變換后的頻譜數(shù)據(jù)復(fù)數(shù)都無(wú)處不在。然而大多數(shù)通用計(jì)算器或編程語(yǔ)言自帶的數(shù)學(xué)庫(kù)對(duì)復(fù)數(shù)的支持往往停留在最基本的四則運(yùn)算上。當(dāng)你需要頻繁地在直角坐標(biāo)abi和極坐標(biāo)模長(zhǎng)r角度θ之間切換或者需要計(jì)算復(fù)數(shù)的模、輻角時(shí)就不得不手動(dòng)進(jìn)行一系列繁瑣的轉(zhuǎn)換和計(jì)算。我最近在做一個(gè)涉及信號(hào)處理的個(gè)人項(xiàng)目就頻繁地遇到了這個(gè)問(wèn)題。我需要驗(yàn)證一系列傳遞函數(shù)的頻率響應(yīng)這意味著要處理大量形如(1 jω)/(2 3jω)的復(fù)數(shù)表達(dá)式并觀察其模值和相位隨頻率ω的變化。用Python的cmath庫(kù)或者M(jìn)ATLAB固然可以但每次都要寫(xiě)腳本或者在交互式環(huán)境里反復(fù)輸入命令對(duì)于快速驗(yàn)證和調(diào)試來(lái)說(shuō)效率并不高。市面上的一些科學(xué)計(jì)算器雖然有復(fù)數(shù)模式但坐標(biāo)轉(zhuǎn)換和結(jié)果顯示的交互方式往往不夠直觀和高效。于是一個(gè)念頭冒了出來(lái)為什么不自己寫(xiě)一個(gè)專用的C復(fù)數(shù)計(jì)算器呢它應(yīng)該能像普通計(jì)算器一樣進(jìn)行交互式輸入但內(nèi)核專門(mén)為復(fù)數(shù)優(yōu)化。核心功能很明確支持復(fù)數(shù)的加、減、乘、除四則運(yùn)算并且能夠以兩種方式表示和輸入復(fù)數(shù)——直角坐標(biāo)形式和極坐標(biāo)形式。同時(shí)對(duì)于任何一個(gè)復(fù)數(shù)都能實(shí)時(shí)顯示它的兩種表示形式以及模長(zhǎng)、角度輻角。這樣一來(lái)無(wú)論是驗(yàn)證公式、快速計(jì)算還是教學(xué)演示都會(huì)方便很多。這個(gè)項(xiàng)目看似簡(jiǎn)單但深入下去會(huì)涉及到C面向?qū)ο笤O(shè)計(jì)、運(yùn)算符重載、數(shù)值計(jì)算精度、用戶交互邏輯等多個(gè)核心知識(shí)點(diǎn)。它不像一個(gè)“黑盒”庫(kù)而是完全透明、可掌控的工具。接下來(lái)我就詳細(xì)拆解這個(gè)計(jì)算器的實(shí)現(xiàn)過(guò)程分享其中遇到的技術(shù)選擇和踩過(guò)的坑。2. 核心設(shè)計(jì)用類來(lái)封裝復(fù)數(shù)的“一體兩面”實(shí)現(xiàn)這個(gè)計(jì)算器的第一步也是最重要的一步就是設(shè)計(jì)一個(gè)能夠完美表示復(fù)數(shù)的類。一個(gè)復(fù)數(shù)本質(zhì)上就是一對(duì)實(shí)數(shù)實(shí)部和虛部但它同時(shí)也可以用模長(zhǎng)和角度這一對(duì)實(shí)數(shù)來(lái)描述。這兩種表示是等價(jià)的可以相互轉(zhuǎn)換。我們的Complex類需要同時(shí)維護(hù)這兩種表示并確保它們?cè)谌魏螘r(shí)刻都是同步的。2.1 Complex類的數(shù)據(jù)成員與構(gòu)造我決定在類內(nèi)部存儲(chǔ)直角坐標(biāo)表示因?yàn)樗膭t運(yùn)算在直角坐標(biāo)下進(jìn)行更為直接和高效。極坐標(biāo)表示則可以通過(guò)成員函數(shù)實(shí)時(shí)計(jì)算得到。這樣避免了同時(shí)維護(hù)兩套數(shù)據(jù)可能帶來(lái)的不一致性問(wèn)題。class Complex { private: double real; // 實(shí)部 double imag; // 虛部 public: // 構(gòu)造函數(shù) Complex(double r 0.0, double i 0.0) : real(r), imag(i) {} // 靜態(tài)工廠方法從極坐標(biāo)創(chuàng)建復(fù)數(shù) static Complex fromPolar(double magnitude, double angle_rad) { return Complex(magnitude * cos(angle_rad), magnitude * sin(angle_rad)); } };這里我提供了一個(gè)默認(rèn)構(gòu)造函數(shù)和一個(gè)雙參數(shù)構(gòu)造函數(shù)。同時(shí)我添加了一個(gè)靜態(tài)工廠方法fromPolar。為什么不直接重載構(gòu)造函數(shù)呢主要是為了語(yǔ)義清晰。Complex(1.0, 2.0)很自然地表示實(shí)部1虛部2。而Complex(5.0, 0.927)如果表示極坐標(biāo)就容易產(chǎn)生歧義0.927是弧度還是度。使用Complex::fromPolar(5.0, 0.927)則一目了然明確告知這是通過(guò)模長(zhǎng)和弧度角來(lái)構(gòu)造。注意三角函數(shù)cos和sin使用的是弧度制。這是數(shù)學(xué)庫(kù)的通用約定我們?cè)谠O(shè)計(jì)接口時(shí)也必須遵守并在文檔或用戶交互中明確提示。2.2 關(guān)鍵的Getter與屬性計(jì)算為了獲取復(fù)數(shù)的各種屬性我們需要一系列成員函數(shù)。這里的一個(gè)設(shè)計(jì)要點(diǎn)是計(jì)算模長(zhǎng)和角度是“廉價(jià)”操作嗎對(duì)于頻繁訪問(wèn)我們是否應(yīng)該緩存結(jié)果考慮到現(xiàn)代CPU計(jì)算速度很快而緩存會(huì)引入狀態(tài)管理的復(fù)雜性我選擇每次訪問(wèn)時(shí)實(shí)時(shí)計(jì)算。這是一個(gè)典型的“用計(jì)算換簡(jiǎn)潔”的權(quán)衡。class Complex { // ... 數(shù)據(jù)成員和構(gòu)造函數(shù)同上 public: // 獲取直角坐標(biāo)分量 double getReal() const { return real; } double getImag() const { return imag; } // 計(jì)算并獲取極坐標(biāo)屬性 double magnitude() const { return sqrt(real * real imag * imag); } double angle() const { // 返回弧度值 if (real 0.0 imag 0.0) { // 復(fù)數(shù)0的輻角未定義通常返回0或拋出異常。這里返回0便于計(jì)算。 return 0.0; } return atan2(imag, real); } // 獲取角度度—— 方便顯示 double angleDegrees() const { return angle() * 180.0 / M_PI; } };這里有幾個(gè)細(xì)節(jié)值得深究magnitude()的計(jì)算直接使用sqrt(real*real imag*imag)。對(duì)于性能極度敏感的場(chǎng)景有更快的近似算法但對(duì)我們這個(gè)計(jì)算器標(biāo)準(zhǔn)庫(kù)的std::sqrt在精度和速度上已經(jīng)足夠。angle()的計(jì)算——為什么用atan2這是整個(gè)項(xiàng)目里第一個(gè)容易踩坑的地方。新手可能會(huì)想用atan(imag / real)。但這有兩個(gè)大問(wèn)題一是當(dāng)real為0時(shí)需要單獨(dú)處理二是atan的值域是(-π/2, π/2)它無(wú)法區(qū)分第二象限和第三象限的點(diǎn)例如復(fù)數(shù) -1i 和 -1-i 的imag/real比值相同。atan2(y, x)函數(shù)完美解決了這兩個(gè)問(wèn)題它接受兩個(gè)參數(shù)能根據(jù)(x, y)所在的象限返回(-π, π]范圍內(nèi)的正確角度。務(wù)必使用atan2(imag, real)而不是atan(imag / real)。零值的處理復(fù)數(shù)0的模長(zhǎng)為0但輻角是未定義的。許多數(shù)學(xué)庫(kù)如C的std::complex規(guī)定arg(0)返回0。為了保持一致性并避免計(jì)算中斷例如在連續(xù)運(yùn)算中我們的angle()函數(shù)也對(duì)(0, 0)返回0。這是一個(gè)設(shè)計(jì)決策需要在注釋或文檔中說(shuō)明。3. 運(yùn)算符重載讓復(fù)數(shù)像基本類型一樣運(yùn)算C的強(qiáng)大之處在于運(yùn)算符重載它能讓我們的Complex類用起來(lái)和int、double一樣自然。我們要重載,-,*,/,,-,*,/以及輸入輸出流和。3.1 四則運(yùn)算的實(shí)現(xiàn)加法和減法非常直觀就是對(duì)應(yīng)分量相加減。乘法和除法則需要套用復(fù)數(shù)運(yùn)算公式。class Complex { // ... 同上 public: // 加法運(yùn)算符重載 (二元作為成員函數(shù)) Complex operator(const Complex other) const { return Complex(real other.real, imag other.imag); } // 減法 Complex operator-(const Complex other) const { return Complex(real - other.real, imag - other.imag); } // 乘法(abi)*(cdi) (ac-bd) (adbc)i Complex operator*(const Complex other) const { return Complex(real * other.real - imag * other.imag, real * other.imag imag * other.real); } // 除法(abi)/(cdi) [(acbd) (bc-ad)i] / (c^2d^2) Complex operator/(const Complex other) const { double denominator other.real * other.real other.imag * other.imag; if (fabs(denominator) 1e-12) { // 處理除數(shù)為零的情況 // 在實(shí)際計(jì)算器中這里應(yīng)該拋出異常或返回一個(gè)表示“無(wú)窮大”的特殊值 // 為簡(jiǎn)單起見(jiàn)這里返回一個(gè)模很大的復(fù)數(shù)模擬“無(wú)窮” std::cerr Warning: Division by (almost) zero complex number.\n; // 返回一個(gè)“無(wú)限大”的近似值注意這不是嚴(yán)格的數(shù)學(xué)定義 double largeNum 1e308; if (denominator 0.0) return Complex(largeNum, largeNum); // 如果分母極小結(jié)果會(huì)極大這里直接放大 return Complex(real / denominator, imag / denominator); // 可能溢出 } return Complex((real * other.real imag * other.imag) / denominator, (imag * other.real - real * other.imag) / denominator); } };除法運(yùn)算的坑復(fù)數(shù)除法的核心是分母c^2 d^2。這里必須做零值檢查。由于浮點(diǎn)數(shù)的精度問(wèn)題我們不能直接判斷denominator 0.0而是判斷其絕對(duì)值是否小于一個(gè)極小的閾值如1e-12。如何處理除零是一個(gè)設(shè)計(jì)問(wèn)題可以拋出std::runtime_error異常在交互式計(jì)算器中捕獲并提示用戶也可以返回一個(gè)特殊的“無(wú)窮大”值如Complex(HUGE_VAL, HUGE_VAL)。上面的代碼給出了一個(gè)簡(jiǎn)單的警告和近似處理在實(shí)際產(chǎn)品中需要更嚴(yán)謹(jǐn)?shù)腻e(cuò)誤處理機(jī)制。3.2 復(fù)合賦值運(yùn)算符的實(shí)現(xiàn),-等運(yùn)算符通常會(huì)修改左操作數(shù)本身并返回自身的引用以支持鏈?zhǔn)秸{(diào)用如a b c。class Complex { // ... 同上 public: // 復(fù)合加法賦值 Complex operator(const Complex other) { real other.real; imag other.imag; return *this; // 返回當(dāng)前對(duì)象的引用 } // 復(fù)合乘法賦值 Complex operator*(const Complex other) { // 注意不能直接用 real * ...因?yàn)橛?jì)算新實(shí)部需要舊的虛部值 double new_real real * other.real - imag * other.imag; double new_imag real * other.imag imag * other.real; real new_real; imag new_imag; return *this; } // 類似的實(shí)現(xiàn) - 和 / };關(guān)鍵點(diǎn)對(duì)于*和/不能像那樣直接修改real和imag因?yàn)樾轮档挠?jì)算依賴于舊值。必須先計(jì)算出臨時(shí)結(jié)果再賦值。這是一個(gè)常見(jiàn)的實(shí)現(xiàn)陷阱。3.3 輸入輸出流的重載為了讓計(jì)算器能方便地讀入和顯示復(fù)數(shù)我們需要重載和。這里要決定復(fù)數(shù)的輸入輸出格式。#include iostream #include sstream #include string class Complex { // ... 同上 // 聲明為友元函數(shù)以便訪問(wèn)私有成員 friend std::ostream operator(std::ostream os, const Complex c); friend std::istream operator(std::istream is, Complex c); }; // 輸出格式例如 3.00 4.00i (mag5.00, ang53.13°) std::ostream operator(std::ostream os, const Complex c) { os.precision(4); // 設(shè)置輸出精度 os std::fixed c.real c.imag i; os (mag c.magnitude() , ang c.angleDegrees() °); return os; } // 輸入格式支持兩種。1) 直角坐標(biāo) a bi 或 a, b。 2) 極坐標(biāo) r θ (θ為度) std::istream operator(std::istream is, Complex c) { std::string input; if (!std::getline(is, input)) { // 讀取整行 is.setstate(std::ios::failbit); return is; } std::istringstream iss(input); double a, b; char plus, i, at_sign; // 用于匹配格式中的字符 // 嘗試解析為直角坐標(biāo) a bi iss a plus b i; if (iss plus (i i || i I)) { c.real a; c.imag b; return is; } // 如果失敗清空狀態(tài)嘗試解析為極坐標(biāo) r θ iss.clear(); iss.str(input); if (iss a at_sign b at_sign ) { // 假設(shè)輸入的角度b是度數(shù)轉(zhuǎn)換為弧度 c Complex::fromPolar(a, b * M_PI / 180.0); return is; } // 如果兩種格式都不匹配設(shè)置失敗標(biāo)志 is.setstate(std::ios::failbit); std::cerr 輸入格式錯(cuò)誤請(qǐng)使用 實(shí)部 虛部i 或 模長(zhǎng) 角度(度) 格式。\n; return is; }輸入解析的復(fù)雜性這是用戶交互的核心也是最容易出問(wèn)題的地方。上面的實(shí)現(xiàn)是一個(gè)簡(jiǎn)化版它要求格式非常嚴(yán)格如號(hào)前后必須有空格。一個(gè)健壯的計(jì)算器需要更強(qiáng)大的解析器能夠處理34i、3,4、553.13、(3,4)等多種格式甚至能識(shí)別j工程學(xué)中常用作為虛數(shù)單位。這通常需要用到正則表達(dá)式或更復(fù)雜的狀態(tài)機(jī)解析。為了項(xiàng)目核心清晰我們先實(shí)現(xiàn)一個(gè)基礎(chǔ)版本但必須意識(shí)到其局限性。4. 計(jì)算器邏輯與用戶交互構(gòu)建可用的外殼有了強(qiáng)大的Complex類接下來(lái)就是構(gòu)建一個(gè)交互式循環(huán)讓用戶能像使用普通計(jì)算器一樣進(jìn)行連續(xù)運(yùn)算。這里的設(shè)計(jì)目標(biāo)是支持連續(xù)運(yùn)算上一次結(jié)果作為下一次運(yùn)算的左操作數(shù)支持清除和退出。4.1 主循環(huán)與狀態(tài)管理#include iostream #include limits #include Complex.h // 假設(shè)我們的類定義在這個(gè)頭文件 void runCalculator() { Complex currentValue(0, 0); // 當(dāng)前值初始為0 Complex operand; // 輸入的右操作數(shù) char operation \0; // 運(yùn)算符 bool running true; std::cout 復(fù)數(shù)計(jì)算器 (輸入 q 退出, c 清除)\n; std::cout 當(dāng)前值: currentValue std::endl; while (running) { std::cout \n請(qǐng)輸入運(yùn)算符 (, -, *, /) 或命令: ; std::string opInput; std::getline(std::cin, opInput); if (opInput.empty()) continue; char opChar opInput[0]; if (opChar q || opChar Q) { running false; continue; } if (opChar c || opChar C) { currentValue Complex(0, 0); std::cout 已清除。當(dāng)前值: currentValue std::endl; continue; } // 檢查是否是有效運(yùn)算符 if (opChar ! opChar ! - opChar ! * opChar ! /) { std::cout 無(wú)效運(yùn)算符或命令。\n; continue; } operation opChar; // 提示輸入右操作數(shù) std::cout 請(qǐng)輸入復(fù)數(shù) (格式: 實(shí)部 虛部i 或 模長(zhǎng) 角度): ; if (!(std::cin operand)) { std::cin.clear(); // 清除錯(cuò)誤狀態(tài) std::cin.ignore(std::numeric_limitsstd::streamsize::max(), \n); // 忽略錯(cuò)誤行 std::cout 輸入無(wú)效請(qǐng)重試。\n; continue; } std::cin.ignore(std::numeric_limitsstd::streamsize::max(), \n); // 消耗掉換行符 // 執(zhí)行運(yùn)算 try { switch (operation) { case : currentValue operand; break; case -: currentValue - operand; break; case *: currentValue * operand; break; case /: // 這里可以加入更精細(xì)的除零判斷 currentValue / operand; break; default: break; // 不會(huì)發(fā)生 } std::cout currentValue std::endl; } catch (const std::exception e) { std::cout 計(jì)算錯(cuò)誤: e.what() std::endl; } } std::cout 計(jì)算器已退出。\n; } int main() { runCalculator(); return 0; }4.2 交互中的細(xì)節(jié)與容錯(cuò)上面的主循環(huán)是一個(gè)基本框架但實(shí)際使用中會(huì)發(fā)現(xiàn)不少問(wèn)題輸入緩沖區(qū)的清理混合使用std::getline和operator時(shí)必須小心處理殘留在輸入緩沖區(qū)中的換行符。std::cin.ignore(...)的使用至關(guān)重要否則會(huì)導(dǎo)致下一次getline直接讀到空行。運(yùn)算錯(cuò)誤處理我們?cè)贑omplex::operator/中做了簡(jiǎn)單的除零檢查。更好的做法是在除法運(yùn)算函數(shù)中拋出標(biāo)準(zhǔn)異常如std::runtime_error然后在主循環(huán)的try-catch塊中捕獲并給用戶友好的提示同時(shí)保持currentValue不變。連續(xù)運(yùn)算的直觀性這個(gè)計(jì)算器是“累加式”的符合許多硬件計(jì)算器的邏輯。但用戶可能想計(jì)算(ab)*(cd)這樣的表達(dá)式。我們的簡(jiǎn)單計(jì)算器不支持運(yùn)算符優(yōu)先級(jí)和括號(hào)它是一個(gè)順序執(zhí)行器。這是功能上的一個(gè)局限也是未來(lái)可以擴(kuò)展的方向?qū)崿F(xiàn)表達(dá)式解析器。歷史記錄與撤銷一個(gè)實(shí)用的計(jì)算器應(yīng)該有歷史記錄和撤銷上一步操作的功能。這可以通過(guò)用一個(gè)std::vectorComplex來(lái)保存歷史狀態(tài)實(shí)現(xiàn)。當(dāng)用戶輸入undo命令時(shí)彈出上一個(gè)狀態(tài)。5. 精度問(wèn)題、邊界條件與測(cè)試任何數(shù)值計(jì)算程序都必須認(rèn)真對(duì)待精度和邊界條件。復(fù)數(shù)計(jì)算器也不例外。5.1 浮點(diǎn)數(shù)精度帶來(lái)的挑戰(zhàn)我們使用double類型存儲(chǔ)實(shí)部和虛部。浮點(diǎn)數(shù)運(yùn)算存在固有的舍入誤差。這會(huì)導(dǎo)致一些理論上成立的性質(zhì)在數(shù)值上出現(xiàn)微小偏差。// 示例單位復(fù)數(shù) i 的平方應(yīng)該是 -1 Complex i(0, 1); Complex result i * i; std::cout result; // 可能輸出-1.0000 0.0000i (mag1.0000, ang180.0°) // 看起來(lái)很好但檢查實(shí)部 std::cout result.getReal(); // 可能輸出-1.0000000000000000 // 實(shí)際上可能是-0.9999999999999999 或 -1.0000000000000002這種誤差在連續(xù)運(yùn)算中會(huì)累積。對(duì)于magnitude()和angle()的計(jì)算影響更大。例如對(duì)于一個(gè)模長(zhǎng)應(yīng)為1的復(fù)數(shù)計(jì)算出的模長(zhǎng)可能是1.0000000000000002。對(duì)于angle()當(dāng)實(shí)部或虛部非常接近0時(shí)atan2的結(jié)果可能對(duì)誤差非常敏感。應(yīng)對(duì)策略比較操作不要用直接比較兩個(gè)Complex對(duì)象。應(yīng)該比較它們的模長(zhǎng)差或各分量差是否小于一個(gè)閾值如1e-10。顯示格式化在輸出時(shí)像上面代碼一樣使用std::fixed和setprecision控制小數(shù)位數(shù)避免顯示一長(zhǎng)串無(wú)意義的數(shù)字。關(guān)鍵判斷在像除法分母判斷這樣的關(guān)鍵位置使用相對(duì)誤差或絕對(duì)誤差閾值而不是直接與0比較。5.2 特殊值的處理我們已經(jīng)討論了復(fù)數(shù)0的輻角問(wèn)題。還有其他邊界情況無(wú)窮大和NaN如果運(yùn)算結(jié)果產(chǎn)生了inf或nan例如除以一個(gè)模長(zhǎng)極小的復(fù)數(shù)我們的類目前沒(méi)有專門(mén)處理。std::complex定義了這些特殊值。我們可以考慮用std::isinf和std::isnan來(lái)檢查real和imag并在輸出時(shí)顯示為inf或nan。角度歸一化atan2返回的角度在(-π, π]之間。但有時(shí)用戶可能希望看到[0, 2π)范圍內(nèi)的角度。可以提供一個(gè)normalizedAngle()函數(shù)來(lái)處理double ang angle(); if (ang 0) ang 2 * M_PI;。5.3 構(gòu)建測(cè)試用例在開(kāi)發(fā)過(guò)程中編寫(xiě)系統(tǒng)的測(cè)試用例是保證質(zhì)量的最好方法。可以創(chuàng)建一個(gè)簡(jiǎn)單的測(cè)試函數(shù)void testComplex() { // 測(cè)試基本運(yùn)算 Complex a(3, 4); // 34i, mag5, ang≈53.13° Complex b(1, -1); // 1-i, mag√2≈1.414, ang-45° assert(fabs((a b).getReal() - 4) 1e-10); assert(fabs((a b).getImag() - 3) 1e-10); Complex c a * b; // (34i)*(1-i) (3*1 - 4*(-1)) (3*(-1)4*1)i 7 1i assert(fabs(c.getReal() - 7) 1e-10); assert(fabs(c.getImag() - 1) 1e-10); // 測(cè)試極坐標(biāo)構(gòu)造 Complex d Complex::fromPolar(5.0, M_PI / 3); // 60度 assert(fabs(d.getReal() - 5.0*cos(M_PI/3)) 1e-10); assert(fabs(d.getImag() - 5.0*sin(M_PI/3)) 1e-10); // 測(cè)試模長(zhǎng)和角度 assert(fabs(a.magnitude() - 5.0) 1e-10); assert(fabs(a.angleDegrees() - 53.13010235415598) 1e-8); std::cout 所有基礎(chǔ)測(cè)試通過(guò)\n; }通過(guò)這樣的測(cè)試我們能在早期發(fā)現(xiàn)operator*公式寫(xiě)錯(cuò)、atan2參數(shù)順序弄反等邏輯錯(cuò)誤。6. 進(jìn)階思考與擴(kuò)展方向一個(gè)基礎(chǔ)可用的復(fù)數(shù)計(jì)算器已經(jīng)完成了。但如果你想讓它更強(qiáng)大、更像一個(gè)專業(yè)工具這里有幾個(gè)擴(kuò)展方向支持更多函數(shù)實(shí)現(xiàn)復(fù)數(shù)的初等函數(shù)如exp,log,sin,cos,sqrt,pow等。這需要依據(jù)復(fù)變函數(shù)的相關(guān)公式。例如復(fù)指數(shù)函數(shù)exp(abi) exp(a) * (cos(b) i*sin(b))。實(shí)現(xiàn)表達(dá)式求值集成一個(gè)簡(jiǎn)單的表達(dá)式解析器如使用調(diào)度場(chǎng)算法支持括號(hào)和運(yùn)算符優(yōu)先級(jí)允許用戶直接輸入(34i)*(1-i)/(230)這樣的表達(dá)式。這會(huì)大大提升易用性。圖形化界面使用Qt、FLTK或ImGui等庫(kù)創(chuàng)建一個(gè)帶按鈕和顯示框的GUI。可以設(shè)計(jì)專門(mén)的按鈕輸入i和并同時(shí)用兩種坐標(biāo)形式顯示結(jié)果。歷史記錄和變量存儲(chǔ)允許用戶將結(jié)果存儲(chǔ)到命名的變量中如A 34i并在后續(xù)表達(dá)式中使用這些變量。精度控制允許用戶選擇使用float,double甚至高精度庫(kù)如GMP來(lái)進(jìn)行計(jì)算以滿足不同場(chǎng)景下的精度和性能需求。單位轉(zhuǎn)換在顯示角度時(shí)提供弧度/度/百分度的快速切換。實(shí)現(xiàn)這個(gè)計(jì)算器的過(guò)程遠(yuǎn)比調(diào)用std::complex要復(fù)雜但收獲也大得多。你不僅鞏固了C類設(shè)計(jì)、運(yùn)算符重載、IO處理等核心知識(shí)更深入地理解了復(fù)數(shù)運(yùn)算的細(xì)節(jié)和浮點(diǎn)數(shù)計(jì)算的陷阱。下次當(dāng)你再需要快速驗(yàn)證一個(gè)復(fù)數(shù)公式時(shí)啟動(dòng)這個(gè)自己親手打造的工具感覺(jué)會(huì)完全不一樣。