泛型数学
来自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
引用
- C11 standard (ISO/IEC 9899:2011):
- 7.25 Type-generic math <tgmath.h> (p: 373-375)
- C99 standard (ISO/IEC 9899:1999):
- 7.22 Type-generic math <tgmath.h> (p: 335-337)