泛型数学 (C99 起)
来自cppreference.com
头文件 <tgmath.h> 包含头文件 <math.h> 及 <complex.h>,并定义了几种泛型宏。这些宏会根据实参类型决定要调用的实际函数。
对于每个宏,在 <math.h> 无后缀版函数中,所对应的实数类型为 double 的形参,即是所谓的泛型形参。(例如,pow 的两个形参都是泛型形参,但 scalbn 只有第一个形参是泛型形参)
如下所述,使用 <tgmath.h> 宏时,传递给泛型形参的实参类型,会决定宏所选择的函数。若实参的类型与所选函数的形参类型不兼容,则行为未定义。(例如,若将复数实参传入实数限定的 <tgmath.h> 宏: 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 变体。
泛型宏如下所示:
实数限定函数
对于所有无复数对应的函数,除 modf
外都存在泛型宏 XXX
,它会调用实数函数变体的中的一种:
- float 变体
XXXf
- double 变体
XXX
- long double 变体
XXXl
以下列方式确定调用的函数:
- 若泛型形参的任一实参为 long double,则调用 long double 变体。否则,若泛型形参的任一实参是 double,则调用 double 变体。否则调用 float 变体。
泛型宏 | 实数函数变体 | ||
---|---|---|---|
float | double | long double | |
atan2 | atan2f | atan2 | atan2l |
cbrt | cbrtf | cbrt | cbrtl |
ceil | ceilf | ceil | ceill |
copysign | copysignf | copysign | copysignl |
erf | erff | erf | erfl |
erfc | erfcf | erfc | erfcl |
exp2 | exp2f | exp2 | exp2l |
expm1 | expm1f | expm1 | expm1l |
fdim | fdimf | fdim | fdiml |
floor | floorf | floor | floorl |
fma | fmaf | fma | fmal |
fmax | fmaxf | fmax | fmaxl |
fmin | fminf | fmin | fminl |
fmod | fmodf | fmod | fmodl |
frexp | frexpf | frexp | frexpl |
hypot | hypotf | hypot | hypotl |
ilogb | ilogbf | ilogb | ilogbl |
ldexp | ldexpf | ldexp | ldexpl |
lgamma | lgammaf | lgamma | lgammal |
llrint | llrintf | llrint | llrintl |
llround | llroundf | llround | llroundl |
log10 | log10f | log10 | log10l |
log1p | log1pf | log1p | log1pl |
log2 | log2f | log2 | log2l |
logb | logbf | logb | logbl |
lrint | lrintf | lrint | lrintl |
lround | lroundf | lround | lroundl |
nearbyint | nearbyintf | nearbyint | nearbyintl |
nextafter | nextafterf | nextafter | nextafterl |
nexttoward | nexttowardf | nexttoward | nexttowardl |
remainder | remainderf | remainder | remainderl |
remquo | remquof | remquo | remquol |
rint | rintf | rint | rintl |
round | roundf | round | roundl |
scalbln | scalblnf | scalbln | scalblnl |
scalbn | scalbnf | scalbn | scalbnl |
tgamma | tgammaf | tgamma | tgammal |
trunc | truncf | trunc | truncl |
复数限定函数
对于所有没有实数对应的复数函数,存在泛型宏 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
引用
- C23 标准(ISO/IEC 9899:2024):
- 7.25 Type-generic math <tgmath.h> (第 TBD 页)
- 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 页)