泛型数学 (C99 起)

来自cppreference.com
< c‎ | numeric


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

上述规则的一个例外是 fabs 宏(见下表)。

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

  • 若泛型形参的任一实参为虚数,则行为会在每个函数参考页面上各自指定。(具体而言,sincostansinhcoshtanhasinatanasinhatanh 调用实数函数,sincostansinhtanhasinatanasinhatanh 的返回类型是虚数,而 coshcosh 的返回类型是实数)
  • 若泛型形参的任一实参为复数,则复数函数会得到调用,否则会调用实数函数。
  • 若泛型形参的任一实参为 long double,则调用 long double 变体。否则,若任一实参是 double 或整数,则调用 double 变体。否则会调用 float 变体。

泛型宏如下所示:

泛型宏实数函数变体复数函数变体
 floatdoublelong doublefloatdoublelong double
fabsfabsffabsfabslcabsfcabscabsl
expexpfexpexplcexpfcexpcexpl
loglogflogloglclogfclogclogl
powpowfpowpowlcpowfcpowcpowl
sqrtsqrtfsqrtsqrtlcsqrtfcsqrtcsqrtl
sinsinfsinsinlcsinfcsincsinl
coscosfcoscoslccosfccosccosl
tantanftantanlctanfctanctanl
asinasinfasinasinlcasinfcasincasinl
acosacosfacosacoslcacosfcacoscacosl
atanatanfatanatanlcatanfcatancatanl
sinhsinhfsinhsinhlcsinhfcsinhcsinhl
coshcoshfcoshcoshlccoshfccoshccoshl
tanhtanhftanhtanhlctanhfctanhctanhl
asinhasinhfasinhasinhlcasinhfcasinhcasinhl
acoshacoshfacoshacoshlcacoshfcacoshcacoshl
atanhatanhfatanhatanhlcatanhfcatanhcatanhl

实数限定函数

对于所有无复数对应的函数,除 modf 外都存在泛型宏 XXX,它会调用实数函数变体的中的一种:

  • float 变体 XXXf
  • double 变体 XXX
  • long double 变体 XXXl

以下列方式确定调用的函数:

  • 若泛型形参的任一实参为 long double,则调用 long double 变体。否则,若泛型形参的任一实参是 double,则调用 double 变体。否则调用 float 变体。
泛型宏实数函数变体
 floatdoublelong double
atan2atan2fatan2atan2l
cbrtcbrtfcbrtcbrtl
ceilceilfceilceill
copysigncopysignfcopysigncopysignl
erferfferferfl
erfcerfcferfcerfcl
exp2exp2fexp2exp2l
expm1expm1fexpm1expm1l
fdimfdimffdimfdiml
floorfloorffloorfloorl
fmafmaffmafmal
fmaxfmaxffmaxfmaxl
fminfminffminfminl
fmodfmodffmodfmodl
frexpfrexpffrexpfrexpl
hypothypotfhypothypotl
ilogbilogbfilogbilogbl
ldexpldexpfldexpldexpl
lgammalgammaflgammalgammal
llrintllrintfllrintllrintl
llroundllroundfllroundllroundl
log10log10flog10log10l
log1plog1pflog1plog1pl
log2log2flog2log2l
logblogbflogblogbl
lrintlrintflrintlrintl
lroundlroundflroundlroundl
nearbyintnearbyintfnearbyintnearbyintl
nextafternextafterfnextafternextafterl
nexttowardnexttowardfnexttowardnexttowardl
remainderremainderfremainderremainderl
remquoremquofremquoremquol
rintrintfrintrintl
roundroundfroundroundl
scalblnscalblnfscalblnscalblnl
scalbnscalbnfscalbnscalbnl
tgammatgammaftgammatgammal
trunctruncftrunctruncl

复数限定函数

对于所有没有实数对应的复数函数,存在泛型宏 cXXX,它会调用复数函数的变体:

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

  • 若泛型形参的任一实参为实数、复数或虚数,则调用适当的复数函数。
泛型宏复数函数变体
 floatdoublelong double
cargcargfcargcargl
conjconjfconjconjl
crealcrealfcrealcreall
cimagcimagfcimagcimagl
cprojcprojfcprojcprojl

示例

#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 页)