復數是 a+bi 形式的數,其中 a 和 b 是真數,在 C++ 代碼中是浮點值,i 是根號 -1。a 被稱作復數的實數部分,b 乘以 i 被稱作虛數部分。
使用復數的程序一般都很專業,例如,復數可以用於電氣和電磁理論、數字信號處理,當然也可以用於數學。復數可以用來生成非常復雜的 Mandelbrot 集合和 Julia 集合的分形圖。
complex 頭文件定義了用於處理復數的功能。complex<T> 模板類型的實例表示的是復數,這里定義了 3 個特化類型:complex<float>、complex<double>、complex<long double>。在這一節中,全部使用 complex<double>,但其他特化類型的操作是基本相同的。
生成表示復數的對象
complex<double> 類型的構造函數接受兩個參數,第一個參數是實部的值,第二個部分是虛部的值。例如:
1 std::complex<double> z1 {2, 5}; // 2 + 5i
2 std::complex<double> z; // Default parameter values, are 0 so 0 + 0i
它也有拷貝構造函數,因此可以按如下方式復制 z1:
std::complex<double> z2 {z1}; // 2 + 5i
顯然,我們需要復數常量以及復數對象,命名空間 std::literals::complex_literals 中定義了 3 個運算符函數,在這個命名空間中,命名空間 literals 和 complex_literals 都是內聯定義的。在對 std::literals::complex_literals、std::literals 或 std::complex_literals 使用 using 指令之后,就可以訪問用於復數常量的運算符函數。假設使用了一個或多個這種指令,並且 using std::complex 對這一節余下的代碼都有效。
運算符 ""i() 函數定義了實部為 0 的 complex<double> 類型的常量。因此,3i 是一個等同於 complex<double>{0,3} 的常量。當然,可以用實部和虛部表示復數。例如:
z = 5.0 + 3i; // z is now complex<double>{5, 3}
這展示了如何定義兩部分都是非零值的復數,並順便說明已經為復數對象實現了賦值運算符。可以對 complex<float> 常量使用后綴if,對 complex<long double> 常量使用后綴il,例如 22if 或 3.5il。這些后綴是由函數 operator""if() 和 operator""il() 定義的。注意,不能寫成 1.0+i 或 2.0+il,因為這里的 i 和 il 會被解釋為變量名,必須寫成 1.0 +li 和 2.0+1.0il。
所有的復數類型都定義了成員函數real()和imag(),它們可以用來訪問對象的實部或虛部,或者用提供的參數設置這些部分。例如:
1 complex<double> z{1.5, -2.5}; // z: 1.5 - 2.5i
2 z.imag(99); // z: 1.5 + 99.0i
3 z.real(-4.5); // z: -4.5 + 99.0i
4 std::cout << "Real part: " << z.real()<< " Imaginary part: " << z.imag()<< std::endl; 5 // Real part: -4.5 Imaginary part: 99
real() 和 imag() 接受參數的版本什么都不會返回。
有為復數對象實現流的插入和提取的非成員函數模板。當從流中讀取一個復數時,它可能只有實部,例如 55,或者括號中只有實部,例如(2.6),或者實部和虛部在由一個逗號隔開的括號中,例如(3,-2)。如果只提供了實部,虛部會為 0。下面是一個示例:
1 complex<double> z1, z2, z3; // 3 default objects 0+0i
2 std:: cout << "Enter 3 complex numbers: "; 3 std::cin >> z1 >> z2 >> z3; // Read 3 complex numbers
4 std::cout << " z1 = " << z1 <<" z2 = " << z2 << " z3 = "<< z3 << std::endl;
下面是示例的輸入和輸出結果:
1 Enter 3 complex numbers: -4 (6) (-3, 7) 2 z1 = (-4,0) z2 = (6,0) z3 = (-3,7)
如果輸入的一個復數沒有括號,就不會有虛部。但是,在括號中可以省略虛部。復數的輸出周圍總是有括號,虛部即使為 0 也會被輸出。
復數的運算
complex 類模板為有復數操作數的二元運算符 +、-、*、/ 及一元 + 和 - 運算符定義了非成員函數。成員函數定義了 +=、-=、*= 和 /=。下面是使用它們的一些示例:
1 complex<double> z {1,2}; // 1+2i
2 auto z1 = z + 3.0; // 4+2i
3 auto z2 = z*z + (2.0 + 4i); // -1+8i
4 auto z3 = z1 - z2; // 5-6i
5 z3 /= z2; // 815385-0.523077i
注意,復數對象和數值常量之間的運算需要數值常量是正確的類型。不能將整數常量加到 complex<double> 對象上;為了能夠進行這個運算,必須寫成 2.0。
復數上的比較和其他運算
一些非成員函數模板可以用來比較兩個復數對象相等或不相等。也有 == 和 !=運算可以用來比較復數對象和數值,這里數值會被看作虛部為 0 的復數。為了相等,所有的部分都必須相等,如果操作數的實部或虛部不同,它們就不相等。例如:
1 complex<double> z1 {3,4}; // 3+4i
2 complex<double> z2 {4,-3}; // 4-3i
3 std::cout << std::boolalpha<<(z1 == z2) << " " // false
4 << (z1 != (3.0 + 4i)) << " " // false
5 << (z2 == 4.0 - 3i) << '\n'; // true
注釋中的結果很清楚。注意在最后一個比較中,編譯器會將 4.0-3i 看作復數。
另一種比較復數的方法是比較它們的量。各部分值和復數的實部及虛部都相同的向量的量和復數相同,是兩部分平分和的平方根。非成員函數模板 abs() 接受 complex<T> 類型的參數,並返回一個T類型的量。下面是一個將 abs() 函數應用到前面的代碼段中定義的 z1 和 z2 上的示例:
1 std::cout << std::boolalpha 2 << (std::abs(z1) == std::abs(z2)) // true
3 << " " <<std::abs(z2 + 4.0 + 9i); // 10
最后的輸出值是 10,因為作為 abs() 的參數的表達式的計算結果是 (8.0+6i);82 和 62 是 100,平方根是 10。
- norm() 函數模板會返回復數的量的平方。
- arg() 模板會返回以弧度為單位的相角,是復數 z 對應的 std::atan(z.imag()/z.real())。
- conj() 函數模板會返回共軛復數,是 a+bi 和 a-bi。
- polar() 函數模板接受量和相角作為參數,並返回和它們對應的復數對象。
- prqj() 函數模板返回的復數是復數參數在黎曼球上的投影。
一些非成員函數模板提供了一整套的三角函數,並為復數參數提供了雙曲函數。也有用於復數參數的 cmath 版本的函數 exp()、pow()、log()、log10() 和 sqrt()。下面是一個有趣的示例:
1 complex<double> zc {0.0, std::acos(-1)}; 2 std::cout << (std::exp (zc) +1.0) << '\n'; // (0, 1.22465e-16) or zero near enough
acos(-1) 是 π,所以這揭示了歐拉方程令人震驚的真相,π 和歐拉數 e 是有關聯的:eiπ+1=0。