Skip to content

tgmath.h

<tgmath.h> 是 C 标准库中的一个头文件,提供了类型泛型数学函数(Type-Generic Math Functions)。

C99 标准中引入,允许开发者使用统一的函数名来调用不同类型的数学函数(如 float、double 和 long double),而无需显式指定函数的具体类型。

主要目的是:

  • 简化数学函数的使用,避免为不同类型(如 float、double、long double)显式调用不同的函数(如 sinf、sin、sinl)。

  • 提高代码的可读性和可维护性。

  • 支持复数类型的数学函数。

类型泛型宏

<tgmath.h> 定义了一组类型泛型宏,这些宏根据参数的类型自动选择正确的数学函数。例如:

sin(x):

  • 如果 x 是 float,则调用 sinf(x);
  • 如果 x 是 double,则调用 sin(x);
  • 如果 x 是 long double,则调用 sinl(x)。

sqrt(x):

  • 如果 x 是 float,则调用 sqrtf(x);
  • 如果 x 是 double,则调用 sqrt(x);
  • 如果 x 是 long double,则调用 sqrtl(x)。

这些宏支持以下类型的参数:

  • 实数类型:float、double、long double。
  • 复数类型:float complex、double complex、long double complex。

支持的函数

基本数学函数

函数描述
sin正弦函数
cos余弦函数
tan正切函数
asin反正弦函数
acos反余弦函数
atan反正切函数
atan2两个参数的反正切函数
sinh双曲正弦函数
cosh双曲余弦函数
tanh双曲正切函数
asinh反双曲正弦函数
acosh反双曲余弦函数
atanh反双曲正切函数

指数和对数函数

函数描述
exp 指数函数
log 自然对数函数
log10 常用对数函数
pow 幂函数

其他函数

函数描述
sqrt平方根函数
fabs绝对值函数
ceil向上取整函数
floor向下取整函数
fmod浮点数取余函数

实现原理

<tgmath.h> 的实现依赖于 C 语言的泛型选择机制(_Generic 关键字)。

_Generic 关键字根据 x 的类型选择正确的函数。

例如,sin 宏的定义可能如下:

cpp
#define sin(x) _Generic((x), \
    float: sinf,             \
    double: sin,             \
    long double: sinl        \
)(x)

示例

示例1

cpp
#include <stdio.h>
#include <tgmath.h>

int main()
{
    // 实数类型
    float f = 1.5f;
    double d = 2.5;
    long double ld = 3.5L;

    // 使用类型泛型函数
    printf("sin(f) = %f\n", sin(f));    // 调用 sinf
    printf("sin(d) = %f\n", sin(d));    // 调用 sin
    printf("sin(ld) = %Lf\n", sin(ld)); // 调用 sinl

    return 0;
}

运行结果

shell
% gcc main.c -o main && ./main
sin(f) = 0.997495
sin(d) = 0.598472
sin(ld) = -0.350783

_Generic

cpp
/**
 * assignment-expression: 赋值表达式,可以认为是变量var
 * generic-assoc-list: 泛型关联表,其语法为:
 *    type-name : expression, 
 *    type-name : expression, 
 *    ..., 
 *    default : expression (可省略)
 */
_Generic (assignment-expression, generic-assoc-list)

示例1

cpp
#include <stdio.h>
#include <fenv.h>
#include <math.h>

#define GET_TYPE_NAME(type) _Generic((type), \
    int: "int",                              \
    long: "long",                            \
    long long: "long long",                  \
    float: "float",                          \
    double: "double",                        \
    long double: "long double")

int main()
{
    char *type_name;
    
    type_name = GET_TYPE_NAME(13);
    printf("Type of 13 is: %s\n", type_name);

    type_name = GET_TYPE_NAME(13.0);
    printf("Type of 13.0 is: %s\n", type_name);
    return 0;
}

运行结果

shell
% gcc main.c -o main && ./main
Type of 13 is: int
Type of 13.0 is: double

示例2

cpp
#include <stdio.h>
#include <fenv.h>
#include <math.h>

int add_int(int x, int y);
float add_float(float x, float y);

#define ADD(x, y) _Generic((x), \
    int: add_int,                  \
    float: add_float)(x, y)

int add_int(int x, int y)
{
    return x + y;
}

float add_float(float x, float y)
{
    return x + y;
}

int main()
{
    int a = 5, b = 10, result_int;
    float c = 5.0, d = 10.0, result_float;

    result_int = ADD(a, b);
    result_float = ADD(c, d);

    printf("Result of adding integers: %d\n", result_int);
    printf("Result of adding floats: %.2f\n", result_float);

    return 0;
}

运行结果

shell
% gcc main.c -o main && ./main
Result of adding integers: 15
Result of adding floats: 15.00