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