# 泛型数学

< c‎ | numeric

头文件 `<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 变体 `cXXXf`
  - double 变体 `cXXX`
  - long double 变体 `cXXXl`

上述规则的一个例外是 `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 变体。

泛型宏 | 实数函数变体
--- | ---
| 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 complex 变体 `cXXXf`
- double complex 变体 `cXXX`
- long double complex 变体 `cXXXl`

调用的函数按以下方式决定：

- 若泛型参数的任一参数为实数、复数或虚数，则调用适当的复数函数。

泛型宏 | 复数函数变体
--- | ---
| 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 标准（ISO/IEC 9899:2011）：

  - 7.25 Type-generic math <tgmath.h> (p: 373-375)

- C99 标准（ISO/IEC 9899:1999）：

  - 7.22 Type-generic math <tgmath.h> (p: 335-337)