<complex.h> Chức năng Số phứcCác hàm số phức trong phần tham chiếu này đều có ba phiên bản: double complex, float complex, và long double complex.
Biến thể float kết thúc bằng f còn biến thể long double kết thúc bằng l, ví dụ với cosine của số phức:
ccos() double complex
ccosf() float complex
ccosl() long double complexBảng dưới chỉ liệt kê phiên bản double complex cho gọn.
| Hàm | Mô tả |
|---|---|
cabs() |
Tính giá trị tuyệt đối của số phức |
cacos() |
Tính arc-cosine phức |
cacosh() |
Tính arc hyperbolic cosine phức |
carg() |
Tính argument phức |
casin() |
Tính arc-sine phức |
casinh() |
Tính arc hyperbolic sine phức |
catan() |
Tính arc-tangent phức |
catanh() |
Tính arc hyperbolic tangent phức |
ccos() |
Tính cosine phức |
ccosh() |
Tính hyperbolic cosine phức |
cexp() |
Tính hàm mũ cơ số \(e\) phức |
cimag() |
Trả về phần ảo của một số phức |
clog() |
Tính logarithm phức |
CMPLX() |
Dựng một giá trị phức từ kiểu thực và ảo |
conj() |
Tính liên hợp của một số phức |
cproj() |
Tính phép chiếu của một số phức |
creal() |
Trả về phần thực của một số phức |
csin() |
Tính sine phức |
csinh() |
Tính hyperbolic sine phức |
csqrt() |
Tính căn bậc hai phức |
ctan() |
Tính tangent phức |
ctanh() |
Tính hyperbolic tangent phức |
Bạn có thể kiểm tra hỗ trợ số phức bằng cách xem macro __STDC_NO_COMPLEX__. Nếu nó được định nghĩa, tức là số phức không có sẵn.
Có thể có hai loại số được định nghĩa: complex và imaginary. Hiện tôi không biết hệ thống nào hiện thực kiểu imaginary cả.
Các kiểu complex, tức một giá trị thực cộng với bội của \(i\), là:
float complex
double complex
long double complexCác kiểu imaginary, tức chỉ chứa bội của \(i\), là:
float imaginary
double imaginary
long double imaginaryGiá trị toán học \(i=\sqrt{-1}\) được biểu diễn bằng ký hiệu _Complex_I hoặc _Imaginary_I, nếu có.
Macro I sẽ được ưu tiên đặt thành _Imaginary_I (nếu có), hoặc là _Complex_I nếu không.
Bạn có thể viết literal ảo (nếu được hỗ trợ) theo cú pháp này:
double imaginary x = 3.4 * I;Bạn có thể viết literal phức bằng ký pháp phức thông thường:
double complex x = 1.2 + 3.4 * I;hoặc dựng chúng bằng macro CMPLX():
double complex x = CMPLX(1.2, 3.4); // Giống 1.2 + 3.4 * ICách sau có lợi thế là xử lý đúng các trường hợp đặc biệt của số phức (như những trường hợp dính vô cùng hoặc số không có dấu) như thể _Imaginary_I đang có mặt, dù thực ra không có.
Mọi giá trị góc đều tính bằng radian.
Một số hàm có các điểm gián đoạn gọi là branch cut (nhát cắt nhánh). Thú thật tôi không phải dân toán nên không bàn nghiêm túc về chuyện này được, nhưng nếu bạn đang ở đây thì tôi tin bạn biết mình đang làm gì ở mảng này.
Nếu hệ của bạn có số không có dấu, bạn có thể biết mình đang ở phía nào của nhát cắt dựa vào dấu. Còn nếu không có thì chịu. Spec viết thêm:
Những hiện thực không hỗ trợ số không có dấu […] không thể phân biệt hai phía của nhát cắt nhánh. Các hiện thực này phải ánh xạ nhát cắt sao cho hàm liên tục khi tiếp cận nhát cắt đi quanh điểm đầu hữu hạn của nhát cắt theo chiều ngược kim đồng hồ. (Các nhát cắt nhánh cho những hàm ở đây đều chỉ có một điểm đầu hữu hạn.) Ví dụ, với hàm căn bậc hai, đi ngược kim đồng hồ quanh điểm đầu hữu hạn của nhát cắt dọc theo trục thực âm thì tiếp cận nhát cắt từ phía trên, vậy nên nhát cắt ánh xạ vào trục ảo dương.
Cuối cùng, có một pragma tên CX_LIMITED_RANGE có thể bật/tắt (mặc định là tắt). Bạn bật nó như thế này:
#pragma STDC CX_LIMITED_RANGE ONNó cho phép một số phép toán trung gian được tràn dưới, tràn trên, hoặc xử lý lỏng tay với vô cùng, đại khái là đánh đổi lấy tốc độ. Nếu bạn chắc chắn những lỗi kiểu đó không xảy ra với các số bạn đang dùng VÀ bạn đang cố vắt kiệt tốc độ, bạn có thể bật macro này lên.
Spec cũng viết thêm:
Mục đích của pragma là cho phép hiện thực dùng các công thức:
\((x+iy)\times(u+iv) = (xu-yv)+i(yu+xv)\)
\((x+iy)/(u+iv) = [(xu+yv)+i(yu-xv)]/(u^2+v^2)\)
\(|x+iy|=\sqrt{x^2+y^2}\)
khi lập trình viên xác định được là chúng an toàn.
cacos(), cacosf(), cacosl()
Tính arc-cosine phức
#include <complex.h>
double complex cacos(double complex z);
float complex cacosf(float complex z);
long double complex cacosl(long double complex z);Tính arc-cosine phức của một số phức.
Số phức z sẽ có phần ảo trong khoảng \([0,\pi]\), còn phần thực không bị chặn.
Có các nhát cắt nhánh nằm ngoài khoảng \([-1,+1]\) trên trục thực.
Trả về arc-cosine phức của z.
#include <stdio.h>
#include <complex.h>
int main(void)
{
double complex x = 8 + 1.5708 * I;
double complex y = cacos(x);
printf("Result: %f + %fi\n", creal(y), cimag(y));
}Output:
Result: 0.195321 + -2.788006icasin(), casinf(), casinl()
Tính arc-sine phức
#include <complex.h>
double complex casin(double complex z);
float complex casinf(float complex z);
long double complex casinl(long double complex z);Tính arc-sine phức của một số phức.
Số phức z sẽ có phần ảo trong khoảng \([-\pi/2,+\pi/2]\), còn phần thực không bị chặn.
Có các nhát cắt nhánh nằm ngoài khoảng \([-1,+1]\) trên trục thực.
Trả về arc-sine phức của z.
#include <stdio.h>
#include <complex.h>
int main(void)
{
double complex x = 8 + 1.5708 * I;
double complex y = casin(x);
printf("Result: %f + %fi\n", creal(y), cimag(y));
}Output:
Result: 1.375476 + 2.788006icatan(), catanf(), catanl()
Tính arc-tangent phức
#include <complex.h>
double complex catan(double complex z);
float complex catanf(float complex z);
long double complex catanl(long double complex z);Tính arc-tangent phức của một số phức.
Số phức z sẽ có phần thực trong khoảng \([-\pi/2,+\pi/2]\), còn phần ảo không bị chặn.
Có các nhát cắt nhánh nằm ngoài khoảng \([-i,+i]\) trên trục ảo.
Trả về arc-tangent phức của z.
#include <stdio.h>
#include <complex.h>
int main(void)
{
double wheat = 8;
double sheep = 1.5708;
double complex x = wheat + sheep * I;
double complex y = catan(x);
printf("Result: %f + %fi\n", creal(y), cimag(y));
}Output:
Result: 1.450947 + 0.023299iccos(), ccosf(), ccosl()
Tính cosine phức
#include <complex.h>
double complex ccos(double complex z);
float complex ccosf(float complex z);
long double complex ccosl(long double complex z);Tính cosine phức của một số phức.
Trả về cosine phức của z.
#include <stdio.h>
#include <complex.h>
int main(void)
{
double complex x = 8 + 1.5708 * I;
double complex y = ccos(x);
printf("Result: %f + %fi\n", creal(y), cimag(y));
}Output:
Result: -0.365087 + -2.276818icsin(), csinf(), csinl()
Tính sine phức
#include <complex.h>
double complex csin(double complex z);
float complex csinf(float complex z);
long double complex csinl(long double complex z);Tính sine phức của một số phức.
Trả về sine phức của z.
#include <stdio.h>
#include <complex.h>
int main(void)
{
double complex x = 8 + 1.5708 * I;
double complex y = csin(x);
printf("Result: %f + %fi\n", creal(y), cimag(y));
}Output:
Result: 2.482485 + -0.334840ictan(), ctanf(), ctanl()
Tính tangent phức
#include <complex.h>
double complex ctan(double complex z);
float complex ctanf(float complex z);
long double complex ctanl(long double complex z);Tính tangent phức của một số phức.
Trả về tangent phức của z.
#include <stdio.h>
#include <complex.h>
int main(void)
{
double complex x = 8 + 1.5708 * I;
double complex y = ctan(x);
printf("Result: %f + %fi\n", creal(y), cimag(y));
}Output:
Result: -0.027073 + 1.085990icacosh(), cacoshf(), cacoshl()
Tính arc hyperbolic cosine phức
#include <complex.h>
double complex cacosh(double complex z);
float complex cacoshf(float complex z);
long double complex cacoshl(long double complex z);Tính arc hyperbolic cosine phức của một số phức.
Có một nhát cắt nhánh tại các giá trị nhỏ hơn \(1\) trên trục thực.
Giá trị trả về sẽ không âm trên trục số thực, và nằm trong khoảng \([-i\pi,+i\pi]\) trên trục ảo.
Trả về arc hyperbolic cosine phức của z.
#include <stdio.h>
#include <complex.h>
int main(void)
{
double complex x = 8 + 1.5708 * I;
double complex y = cacosh(x);
printf("Result: %f + %fi\n", creal(y), cimag(y));
}Output:
Result: 2.788006 + 0.195321icasinh(), casinhf(), casinhl()
Tính arc hyperbolic sine phức
#include <complex.h>
double complex casinh(double complex z);
float complex casinhf(float complex z);
long double complex casinhl(long double complex z);Tính arc hyperbolic sine phức của một số phức.
Có các nhát cắt nhánh nằm ngoài \([-i,+i]\) trên trục ảo.
Giá trị trả về không bị chặn trên trục số thực, và nằm trong khoảng \([-i\pi/2,+i\pi/2]\) trên trục ảo.
Trả về arc hyperbolic sine phức của z.
#include <stdio.h>
#include <complex.h>
int main(void)
{
double complex x = 8 + 1.5708 * I;
double complex y = casinh(x);
printf("Result: %f + %fi\n", creal(y), cimag(y));
}Output:
Result: 2.794970 + 0.192476icatanh(), catanhf(), catanhl()
Tính arc hyperbolic tangent phức
#include <complex.h>
double complex catanh(double complex z);
float complex catanhf(float complex z);
long double complex catanhl(long double complex z);Tính arc hyperbolic tangent phức của một số phức.
Có các nhát cắt nhánh nằm ngoài \([-1,+1]\) trên trục thực.
Giá trị trả về không bị chặn trên trục số thực, và nằm trong khoảng \([-i\pi/2,+i\pi/2]\) trên trục ảo.
Trả về arc hyperbolic tangent phức của z.
#include <stdio.h>
#include <complex.h>
int main(void)
{
double complex x = 8 + 1.5708 * I;
double complex y = catanh(x);
printf("Result: %f + %fi\n", creal(y), cimag(y));
}Output:
Result: 0.120877 + 1.546821iccosh(), ccoshf(), ccoshl()
Tính hyperbolic cosine phức
#include <complex.h>
double complex ccosh(double complex z);
float complex ccoshf(float complex z);
long double complex ccoshl(long double complex z);Tính hyperbolic cosine phức của một số phức.
Trả về hyperbolic cosine phức của z.
#include <stdio.h>
#include <complex.h>
int main(void)
{
double complex x = 8 + 1.5708 * I;
double complex y = ccosh(x);
printf("Result: %f + %fi\n", creal(y), cimag(y));
}Output:
Result: -0.005475 + 1490.478826icsinh(), csinhf(), csinhl()
Tính hyperbolic sine phức
#include <complex.h>
double complex csinh(double complex z);
float complex csinhf(float complex z);
long double complex csinhl(long double complex z);Tính hyperbolic sine phức của một số phức.
Trả về hyperbolic sine phức của z.
#include <stdio.h>
#include <complex.h>
int main(void)
{
double complex x = 8 + 1.5708 * I;
double complex y = csinh(x);
printf("Result: %f + %fi\n", creal(y), cimag(y));
}Output:
Result: -0.005475 + 1490.479161ictanh(), ctanhf(), ctanhl()
Tính hyperbolic tangent phức
#include <complex.h>
double complex ctanh(double complex z);
float complex ctanhf(float complex z);
long double complex ctanhl(long double complex z);Tính hyperbolic tangent phức của một số phức.
Trả về hyperbolic tangent phức của z.
#include <stdio.h>
#include <complex.h>
int main(void)
{
double complex x = 8 + 1.5708 * I;
double complex y = ctanh(x);
printf("Result: %f + %fi\n", creal(y), cimag(y));
}Output:
Result: 1.000000 + -0.000000icexp(), cexpf(), cexpl()
Tính hàm mũ cơ số \(e\) phức
#include <complex.h>
double complex cexp(double complex z);
float complex cexpf(float complex z);
long double complex cexpl(long double complex z);Tính hàm mũ cơ số \(e\) phức của z.
Trả về hàm mũ cơ số \(e\) phức của z.
#include <stdio.h>
#include <complex.h>
int main(void)
{
double complex x = 1 + 2 * I;
double complex y = cexp(x);
printf("Result: %f + %fi\n", creal(y), cimag(y));
}Output:
Result: -1.131204 + 2.471727iclog(), clogf(), clogl()
Tính logarithm phức
#include <complex.h>
double complex clog(double complex z);
float complex clogf(float complex z);
long double complex clogl(long double complex z);Tính logarithm cơ số \(e\) phức của z. Có một nhát cắt nhánh trên trục thực âm.
Giá trị trả về không bị chặn trên trục thực và nằm trong khoảng \([-i\pi,+i\pi]\) trên trục ảo.
Trả về logarithm cơ số \(e\) phức của z.
#include <stdio.h>
#include <complex.h>
int main(void)
{
double complex x = 1 + 2 * I;
double complex y = clog(x);
printf("Result: %f + %fi\n", creal(y), cimag(y));
}Output:
Result: 0.804719 + 1.107149icabs(), cabsf(), cabsl()
Tính giá trị tuyệt đối phức
#include <complex.h>
double cabs(double complex z);
float cabsf(float complex z);
long double cabsl(long double complex z);Tính giá trị tuyệt đối phức của z.
Trả về giá trị tuyệt đối phức của z.
#include <stdio.h>
#include <complex.h>
int main(void)
{
double complex x = 1 + 2 * I;
double complex y = cabs(x);
printf("Result: %f + %fi\n", creal(y), cimag(y));
}Output:
Result: 2.236068 + 0.000000icpow(), cpowf(), cpowl()
Tính luỹ thừa phức
#include <complex.h>
double complex cpow(double complex x, double complex y);
float complex cpowf(float complex x, float complex y);
long double complex cpowl(long double complex x,
long double complex y);Tính \(x^y\) phức.
Có một nhát cắt nhánh cho x dọc theo trục thực âm.
Trả về \(x^y\) phức.
#include <stdio.h>
#include <complex.h>
int main(void)
{
double complex x = 1 + 2 * I;
double complex y = 3 + 4 * I;
double r = cpow(x, y);
printf("Result: %f + %fi\n", creal(r), cimag(r));
}Result:
Result: 0.129010 + 0.000000icsqrt(), csqrtf(), csqrtl()
Tính căn bậc hai phức
#include <complex.h>
double complex csqrt(double complex z);
float complex csqrtf(float complex z);
long double complex csqrtl(long double complex z);Tính căn bậc hai phức của z.
Có một nhát cắt nhánh dọc theo trục thực âm.
Giá trị trả về nằm ở nửa phải của mặt phẳng phức và bao gồm cả trục ảo.
Trả về căn bậc hai phức của z.
#include <stdio.h>
#include <complex.h>
int main(void)
{
double complex x = 1 + 2 * I;
double complex y = csqrt(x);
printf("Result: %f + %fi\n", creal(y), cimag(y));
}Output:
Result: 1.272020 + 0.786151icarg(), cargf(), cargl()
Tính argument phức
#include <complex.h>
double carg(double complex z);
float cargf(float complex z);
long double cargl(long double complex z);Tính argument phức (còn gọi là góc pha) của z.
Có một nhát cắt nhánh dọc theo trục thực âm.
Trả về giá trị trong khoảng \([-\pi,+\pi]\).
Trả về argument phức của z.
#include <stdio.h>
#include <complex.h>
int main(void)
{
double complex x = 1 + 2 * I;
double y = carg(x);
printf("Result: %f\n", y);
}Output:
Result: 1.107149cimag(), cimagf(), cimagl()
Trả về phần ảo của một số phức
#include <complex.h>
double cimag(double complex z);
float cimagf(float complex z);
long double cimagl(long double complex z);Trả về phần ảo của z.
Nói ngoài lề, spec chỉ ra rằng bất kỳ số phức x nào cũng tuân theo đẳng thức sau:
x == creal(x) + cimag(x) * I;Trả về phần ảo của z.
#include <stdio.h>
#include <complex.h>
int main(void)
{
double complex x = 1 + 2 * I;
double y = cimag(x);
printf("Result: %f\n", y);
}Output—chỉ phần ảo:
Result: 2.000000CMPLX(), CMPLXF(), CMPLXL()
Dựng một giá trị phức từ kiểu thực và ảo
#include <complex.h>
double complex CMPLX(double x, double y);
float complex CMPLXF(float x, float y);
long double complex CMPLXL(long double x, long double y);Các macro này dựng một giá trị phức từ các kiểu thực và ảo.
Chắc là bạn đang nghĩ, “Nhưng tôi đã có thể dựng giá trị phức từ kiểu thực và ảo bằng macro I rồi mà, như trong ví dụ sắp tới.”
double complex x = 1 + 2 * I;Và điều đó đúng.
Nhưng thực tế vấn đề lạ và phức.
Có thể I đã bị undefine, hoặc có thể bạn đã redefine nó.
Hoặc có thể I được định nghĩa là _Complex_I, thứ không nhất thiết giữ được dấu của giá trị không.
Như spec chỉ ra, các macro này dựng số phức như thể _Imaginary_I đang có mặt (nhờ đó giữ được dấu không của bạn) ngay cả khi nó không có. Cụ thể, chúng được định nghĩa tương đương với:
#define CMPLX(x, y) ((double complex)((double)(x) + \
_Imaginary_I * (double)(y)))
#define CMPLXF(x, y) ((float complex)((float)(x) + \
_Imaginary_I * (float)(y)))
#define CMPLXL(x, y) ((long double complex)((long double)(x) + \
_Imaginary_I * (long double)(y)))Trả về số phức cho các phần thực x và ảo y đã cho.
#include <stdio.h>
#include <complex.h>
int main(void)
{
double complex x = CMPLX(1, 2); // Giống 1 + 2 * I
printf("Result: %f + %fi\n", creal(x), cimag(x));
}Output:
Result: 1.000000 + 2.000000iconj(), conjf(), conjl()
Tính liên hợp của một số phức
#include <complex.h>
double complex conj(double complex z);
float complex conjf(float complex z);
long double complex conjl(long double complex z);Hàm này tính liên hợp phức13 của z. Hình như nó làm vậy bằng cách đảo dấu phần ảo, nhưng trời ạ, tôi là lập trình viên chứ không phải dân toán, Jim ơi!
Trả về liên hợp phức của z
#include <stdio.h>
#include <complex.h>
int main(void)
{
double complex x = 1 + 2 * I;
double complex y = conj(x);
printf("Result: %f + %fi\n", creal(y), cimag(y));
}Output:
Result: 1.000000 + -2.000000icproj(), cproj(), cproj()
Tính phép chiếu của một số phức
#include <complex.h>
double complex cproj(double complex z);
float complex cprojf(float complex z);
long double complex cprojl(long double complex z);Tính phép chiếu của z lên một mặt cầu Riemann14.
Giờ thì thực sự ngoài vùng chuyên môn của tôi rồi. Spec viết thế này, tôi trích nguyên văn vì không đủ hiểu biết để viết lại cho gọn. Mong là nó hiểu được với ai cần dùng hàm này.
zchiếu thànhztrừ khi mọi vô cùng phức (kể cả những vô cùng có một phần vô cùng và một phầnNaN) đều chiếu thành vô cùng dương trên trục thực. Nếuzcó phần vô cùng, thìcproj(z)tương đương vớiINFINITY + I * copysign(0.0, cimag(z))
Đấy, có vậy thôi.
Trả về phép chiếu của z lên một mặt cầu Riemann.
Bắt chéo ngón tay cầu cho ví dụ này có tí hợp lý…
#include <stdio.h>
#include <complex.h>
#include <math.h>
int main(void)
{
double complex x = 1 + 2 * I;
double complex y = cproj(x);
printf("Result: %f + %fi\n", creal(y), cimag(y));
x = INFINITY + 2 * I;
y = cproj(x);
printf("Result: %f + %fi\n", creal(y), cimag(y));
}Output:
Result: 1.000000 + 2.000000i
Result: inf + 0.000000icreal(), crealf(), creall()
Trả về phần thực của một số phức
#include <complex.h>
double creal(double complex z);
float crealf(float complex z);
long double creall(long double complex z);Trả về phần thực của z.
Nói ngoài lề, spec chỉ ra rằng bất kỳ số phức x nào cũng tuân theo đẳng thức sau:
x == creal(x) + cimag(x) * I;Trả về phần thực của z.
#include <stdio.h>
#include <complex.h>
int main(void)
{
double complex x = 1 + 2 * I;
double y = creal(x);
printf("Result: %f\n", y);
}Output—chỉ phần thực:
Result: 1.000000