泛型数学
来自cppreference.com
头文件 <tgmath.h>
包含头文件 <math.h>
及 <complex.h>
,并定义了几种泛型宏。这些宏会根据实参类型决定要调用的实际函数。
对于每个宏,在 <math.h>
无后缀版函数中,所对应的实数类型为 double 的形参,即是所谓的泛型形参。(例如,pow 的两个形参都是泛型形参,但 scalbn 只有第一个形参是泛型形参)
如下所述,使用 <tgmath.h>
宏时,传递给泛型形参的实参类型,会决定宏所选择的函数。若实参的类型与所选函数的形参类型不兼容,则行为未定义。(例如,若将复数实参传入实数限定的 tgmath 宏: float complex fc; ceil(fc) 或 double complex dc; double d; fmax(dc, d) 就是未定义行为的例子)
注意:泛型宏在 C99 中曾以实现定义行为实现,但 C11 关键词 _Generic 使得以可移植方式实现这些宏成为可能。
复数/实数泛型宏
对于所有拥有实数及复数对应的函数,存在泛型宏,调用下列函数之一:
- 实数函数:
- float 变体
XXXf
- double 变体
XXX
- long double 变体
XXXl
- float 变体
- 复数函数:
- float 变体
cXXXf
- double 变体
cXXX
- long double 变体
cXXXl
- float 变体
上述规则的一个例外是 fabs
宏(见下表)。
按以下方式决定调用的函数:
- 若泛型形参的任一实参为虚数,则行为会在每个函数参考页面上各自指定。(具体而言,
sin
、cos
、tan
、sinh
、cosh
、tanh
、asin
、atan
、asinh
及atanh
调用实数函数,sin
、cos
、tan
、sinh
、tanh
、asin
、atan
、asinh
及atanh
的返回类型是虚数,而cosh
与cosh
的返回类型是实数) - 若泛型形参的任一实参为复数,则复数函数会得到调用,否则会调用实数函数。
- 若泛型形参的任一实参为 long double,则调用 long double 变体。否则,若任一实参是 double 或整数,则调用 double 变体。否则会调用 float 变体。
泛型宏如下所示:
泛型宏 | 实数函数变体 | 复数函数变体 | ||||
---|---|---|---|---|---|---|
float |
double |
long double |
float |
double |
long double | |
fabs | fabsf | fabs | fabsl | cabsf | cabs | cabsl |
exp | expf | exp | expl | cexpf | cexp | cexpl |
log | logf | log | logl | clogf | clog | clogl |
pow | powf | pow | powl | cpowf | cpow | cpowl |
sqrt | sqrtf | sqrt | sqrtl | csqrtf | csqrt | csqrtl |
sin | sinf | sin | sinl | csinf | csin | csinl |
cos | cosf | cos | cosl | ccosf | ccos | ccosl |
tan | tanf | tan | tanl | ctanf | ctan | ctanl |
asin | asinf | asin | asinl | casinf | casin | casinl |
acos | acosf | acos | acosl | cacosf | cacos | cacosl |
atan | atanf | atan | atanl | catanf | catan | catanl |
sinh | sinhf | sinh | sinhl | csinhf | csinh | csinhl |
cosh | coshf | cosh | coshl | ccoshf | ccosh | ccoshl |
tanh | tanhf | tanh | tanhl | ctanhf | ctanh | ctanhl |
asinh | asinhf | asinh | asinhl | casinhf | casinh | casinhl |
acosh | acoshf | acosh | acoshl | cacoshf | cacosh | cacoshl |
atanh | atanhf | atanh | atanhl | catanhf | catanh | catanhl |
实数限定函数
对于所有无复数对应的函数,除 modf
外都存在泛型宏 XXX
,它会调用实数函数变体的中的一种:
- float 变体
XXXf
- double 变体
XXX
- long double 变体
XXXl
以下列方式确定调用的函数:
- 若泛型形参的任一实参为 long double,则调用 long double 变体。否则,若泛型形参的任一实参是 double,则调用 double 变体。否则调用 float 变体。
复数限定函数
对于所有没有实数对应的复数函数,存在泛型宏 cXXX
,它会调用复数函数的变体:
调用的函数按以下方式决定:
- 若泛型形参的任一实参为实数、复数或虚数,则调用适当的复数函数。
泛型宏 | 复数函数变体 | ||
---|---|---|---|
float |
double |
long double | |
carg | cargf | carg | cargl |
conj | conjf | conj | conjl |
creal | crealf | creal | creall |
cimag | cimagf | cimag | cimagl |
cproj | cprojf | cproj | cprojl |
示例
运行此代码
#include <stdio.h> #include <tgmath.h> int main(void) { int i = 2; printf("sqrt(2) = %f\n", sqrt(i)); // 实参类型为 int,调用 sqrt float f = 0.5; printf("sin(0.5f) = %f\n", sin(f)); // 实参类型为 float,调用 sinf float complex dc = 1 + 0.5*I; float complex z = sqrt(dc); // 实参类型为 float complex,调用 csqrtf printf("sqrt(1 + 0.5i) = %f+%fi\n", creal(z), // 实参类型为 float complex,调用 crealf cimag(z)); // 实参类型为 float complex,调用 cimagf }
输出:
sqrt(2) = 1.414214 sin(0.5f) = 0.479426 sqrt(1 + 0.5i) = 1.029086+0.242934i
引用
- C17 标准(ISO/IEC 9899:2018):
- 7.25 Type-generic math <tgmath.h> (第 272-273 页)
- C11 标准(ISO/IEC 9899:2011):
- 7.25 Type-generic math <tgmath.h> (第 373-375 页)
- C99 标准(ISO/IEC 9899:1999):
- 7.22 Type-generic math <tgmath.h> (第 335-337 页)