C编程
<math.h>
头文件包含了多个数学函数的原型。在 1990 年版的 ISO 标准中,仅指定了这些函数的 double
版本;1999 年版则添加了 float
和 long
double
版本。要使用这些数学函数,必须将程序与数学库链接。对于某些编译器(包括 GCC),你需要指定额外的参数 -lm
[1][2]。
数学函数可能会产生两种类型的错误。域错误发生在函数的参数无效时,例如对 sqrt
(平方根函数)传递一个负数参数。范围错误发生在函数的结果无法在特定的浮点类型中表示时,例如当 double
的最大值大约为 10308
时,pow(1000.0,
1000.0)
会导致范围错误。
这些函数可以分为以下几类:
三角函数
acos
和 asin
函数
acos
函数返回其参数的反余弦值(弧度),asin
函数返回其参数的反正弦值(弧度)。所有这些函数的参数必须在 [-1, +1] 范围内。反余弦函数返回值的范围是 [0, π];反正弦函数返回值的范围是 [-π/2, +π/2]。
#include <math.h>
float asinf(float x); /* C99 */
float acosf(float x); /* C99 */
double asin(double x);
double acos(double x);
long double asinl(long double x); /* C99 */
long double acosl(long double x); /* C99 */
atan
和 atan2
函数
atan
函数返回其参数的反正切值(弧度),atan2
函数返回 y/x
的反正切值(弧度)。atan
函数返回值的范围是 [-π/2, +π/2](原因是浮点数可能表示无穷大,atan(±∞) = ±π/2
);atan2
函数返回值的范围是 [-π, +π]。对于 atan2
,如果两个参数都是零,可能会发生域错误。
#include <math.h>
float atanf(float x); /* C99 */
float atan2f(float y, float x); /* C99 */
double atan(double x);
double atan2(double y, double x);
long double atanl(long double x); /* C99 */
long double atan2l(long double y, long double x); /* C99 */
cos
、sin
和 tan
函数
cos
、sin
和 tan
函数分别返回参数的余弦值、正弦值和正切值,单位为弧度。
#include <math.h>
float cosf(float x); /* C99 */
float sinf(float x); /* C99 */
float tanf(float x); /* C99 */
double cos(double x);
double sin(double x);
double tan(double x);
long double cosl(long double x); /* C99 */
long double sinl(long double x); /* C99 */
long double tanl(long double x); /* C99 */
双曲函数
cosh
、sinh
和 tanh
函数分别计算参数的双曲余弦、双曲正弦和双曲正切值。对于双曲正弦和双曲余弦函数,如果参数的绝对值太大,可能会发生范围错误。
acosh
函数计算参数的反双曲余弦值。如果参数小于 1,将发生域错误。
asinh
函数计算参数的反双曲正弦值。
atanh
函数计算参数的反双曲正切值。如果参数不在 [-1, +1] 范围内,将发生域错误。如果参数等于 -1 或 +1,可能会发生范围错误。
#include <math.h>
float coshf(float x); /* C99 */
float sinhf(float x); /* C99 */
float tanhf(float x); /* C99 */
double cosh(double x);
double sinh(double x);
double tanh(double x);
long double coshl(long double x); /* C99 */
long double sinhl(long double x); /* C99 */
long double tanhl(long double x); /* C99 */
float acoshf(float x); /* C99 */
float asinhf(float x); /* C99 */
float atanhf(float x); /* C99 */
double acosh(double x); /* C99 */
double asinh(double x); /* C99 */
double atanh(double x); /* C99 */
long double acoshl(long double x); /* C99 */
long double asinhl(long double x); /* C99 */
long double atanhl(long double x); /* C99 */
指数和对数函数
exp
、exp2
和 expm1
函数
exp
函数计算 x
的以 e
为底的指数值(e^x
)。如果 x
的绝对值太大,会发生范围错误。
exp2
函数计算 x
的以 2 为底的指数值(2^x
)。如果 x
的绝对值太大,会发生范围错误。
expm1
函数计算 x
的以 e
为底的指数值减去 1(e^x
- 1
)。如果 x
的绝对值太大,会发生范围错误。
#include <math.h>
float expf(float x); /* C99 */
double exp(double x);
long double expl(long double x); /* C99 */
float exp2f(float x); /* C99 */
double exp2(double x); /* C99 */
long double exp2l(long double x); /* C99 */
float expm1f(float x); /* C99 */
double expm1(double x); /* C99 */
long double expm1l(long double x); /* C99 */
exp
函数计算 x
的自然指数值(e^x
)。如果 x
的绝对值过大,将产生范围错误。
exp2
函数计算以 2 为底的指数值(2^x
)。如果 x
的绝对值过大,将产生范围错误。
expm1
函数计算 x
的自然指数值减去 1(e^x - 1
)。如果 x
的绝对值过大,将产生范围错误。
frexp
、ldexp
、modf
、scalbn
和 scalbln
函数
这些函数在软件浮点仿真器中被广泛使用,但在其他情况下很少直接调用。
在计算机内部,每个浮点数由两个部分表示:
- 尾数在 [1/2, 1) 范围内,或者等于零。
- 指数是一个整数。
浮点数的值 v
可表示为: v=significand×2exponentv = \text{significand} \times 2^{\text{exponent}}
frexp
函数将浮点数拆分为尾数和指数。它将指数存储在 ex
指向的整数对象中,并返回尾数。换句话说,返回的值是给定浮点数的副本,但其指数被替换为 0。如果值为 0,则结果的两个部分均为零。
ldexp
函数将浮点数乘以 2 的某个整数次方,并返回结果。换句话说,它返回给定浮点数的副本,指数增加了 ex
。如果指数过大,可能会发生范围错误。
modf
函数将值拆分为整数部分和小数部分,两者的符号与参数相同。它将整数部分存储在 *iptr
指向的对象中,并返回小数部分。*iptr
是一个浮点类型,而不是 int
类型,因为它可能存储类似 1 000 000 000 000 000 000 000
这样太大的整数,无法容纳在 int
中。
scalbn
和 scalbln
函数计算 x ×
FLT_RADIX^n
。FLT_RADIX
是浮点数系统的基数;如果它是 2,则这两个函数与 ldexp
等效。
#include <math.h>
float frexpf(float value, int *ex); /* C99 */
double frexp(double value, int *ex);
long double frexpl(long double value, int *ex); /* C99 */
float ldexpf(float x, int ex); /* C99 */
double ldexp(double x, int ex);
long double ldexpl(long double x, int ex); /* C99 */
float modff(float value, float *iptr); /* C99 */
double modf(double value, double *iptr);
long double modfl(long double value, long double *iptr); /* C99 */
float scalbnf(float x, int ex); /* C99 */
double scalbn(double x, int ex); /* C99 */
long double scalbnl(long double x, int ex); /* C99 */
float scalblnf(float x, long int ex); /* C99 */
double scalbln(double x, long int ex); /* C99 */
long double scalblnl(long double x, long int ex); /* C99 */
log
、log2
、log1p
和 log10
函数
log
函数计算给定参数的自然对数,并返回结果。如果参数为负数,则会发生域错误;如果参数为零,则可能会发生范围错误。
log1p
函数计算 1 + x
的自然对数,并返回结果。如果参数小于 -1,则会发生域错误;如果参数为 -1,则可能会发生范围错误。
log10
函数计算给定参数的常用对数(以 10 为底),并返回结果。如果参数为负数,则会发生域错误;如果参数为零,则可能会发生范围错误。
log2
函数计算给定参数的以 2 为底的对数,并返回结果。如果参数为负数,则会发生域错误;如果参数为零,则可能会发生范围错误。
#include <math.h>
float logf(float x); /* C99 */
double log(double x);
long double logl(long double x); /* C99 */
float log1pf(float x); /* C99 */
double log1p(double x); /* C99 */
long double log1pl(long double x); /* C99 */
float log10f(float x); /* C99 */
double log10(double x);
long double log10l(long double x); /* C99 */
float log2f(float x); /* C99 */
double log2(double x); /* C99 */
long double log2l(long double x); /* C99 */
ilogb
和 logb
函数
ilogb
函数提取 x
的指数值,并作为一个有符号整数返回。如果 x
为零,则返回 FP_ILOGB0
;如果 x
是无穷大,则返回 INT_MAX
;如果 x
是非数值(NaN),则返回 FP_ILOGBNAN
。否则,它相当于调用相应的 logb
函数,并将返回值强制转换为整数类型。若 x
为零,则可能发生范围错误。FP_ILOGB0
和 FP_ILOGBNAN
是在 math.h
中定义的宏;INT_MAX
是在 limits.h
中定义的宏。
logb
函数提取 x
的指数值,并作为浮点数格式的有符号整数返回。如果 x
是亚正常数,它将被当作标准数处理;因此,对于正的有限 x
,满足 1 ≤ x × FLT_RADIX - logb(x)
< FLT_RADIX
,其中 FLT_RADIX
是浮点数的基数,在 float.h
中定义。
#include <math.h>
int ilogbf(float x); /* C99 */
int ilogb(double x); /* C99 */
int ilogbl(long double x); /* C99 */
float logbf(float x); /* C99 */
double logb(double x); /* C99 */
long double logbl(long double x); /* C99 */
幂运算函数
pow
函数
pow
函数计算 x
的 y
次幂,并返回结果。如果 x
为负且 y
不是整数,则会发生域错误。如果 x
为零且 y
小于或等于零,也会发生域错误。可能会发生范围错误。
#include <math.h>
float powf(float x, float y); /* C99 */
double pow(double x, double y);
long double powl(long double x, long double y); /* C99 */
sqrt
函数
sqrt
函数计算 x
的正平方根,并返回结果。如果参数为负数,则会发生域错误。
#include <math.h>
float sqrtf(float x); /* C99 */
double sqrt(double x);
long double sqrtl(long double x); /* C99 */
cbrt
函数
cbrt
函数计算 x
的立方根,并返回结果。
#include <math.h>
float cbrtf(float x); /* C99 */
double cbrt(double x); /* C99 */
long double cbrtl(long double x); /* C99 */
hypot
函数
hypot
函数计算 x
和 y
的平方和的平方根,避免溢出或下溢,并返回结果。
#include <math.h>
float hypotf(float x, float y); /* C99 */
double hypot(double x, double y); /* C99 */
long double hypotl(long double x, long double y); /* C99 */
最近整数、绝对值和余数函数
ceil 和 floor 函数ceil
函数计算不小于 x
的最小整数值并返回结果;floor
函数计算不大于 x
的最大整数值并返回结果。
#include <math.h>
float ceilf(float x); /* C99 */
double ceil(double x);
long double ceill(long double x); /* C99 */
float floorf(float x); /* C99 */
double floor(double x);
long double floorl(long double x); /* C99 */
fabs 函数fabs
函数计算浮动点数 x
的绝对值并返回结果。
#include <math.h>
float fabsf(float x); /* C99 */
double fabs(double x);
long double fabsl(long double x); /* C99 */
fmod 函数fmod
函数计算浮动点数 x/y
的余数并返回值 x - i * y
,其中 i
是某个整数,使得如果 y
非零,结果与 x
同符号,并且其大小小于 y
的大小。如果 y
为零,是否发生域错误或 fmod
函数返回零是实现定义的。
#include <math.h>
float fmodf(float x, float y); /* C99 */
double fmod(double x, double y);
long double fmodl(long double x, long double y); /* C99 */
nearbyint, rint, lrint, 和 llrint 函数nearbyint
函数将其参数舍入为浮动点格式的整数值,使用当前舍入方向且不会引发“不精确”的浮动点异常。
rint
函数与 nearbyint
函数类似,但如果结果与参数的值不同,则可以引发“不精确”浮动点异常。
lrint
和 llrint
函数根据当前舍入方向将其参数舍入为最接近的整数值。如果结果超出了返回类型的值范围,结果是未定义的,如果参数的大小过大,可能会发生范围错误。
#include <math.h>
float nearbyintf(float x); /* C99 */
double nearbyint(double x); /* C99 */
long double nearbyintl(long double x); /* C99 */
float rintf(float x); /* C99 */
double rint(double x); /* C99 */
long double rintl(long double x); /* C99 */
long int lrintf(float x); /* C99 */
long int lrint(double x); /* C99 */
long int lrintl(long double x); /* C99 */
long long int llrintf(float x); /* C99 */
long long int llrint(double x); /* C99 */
long long int llrintl(long double x); /* C99 */
round, lround, 和 llround 函数round
函数将参数舍入为最接近的整数值,舍入时对于半数情况会远离零,而不管当前舍入方向。
lround
和 llround
函数将参数舍入为最接近的整数值,同样对于半数情况会远离零,而不管当前舍入方向。如果结果超出了返回类型的值范围,结果是未定义的,如果参数的大小过大,可能会发生范围错误。
#include <math.h>
float roundf(float x); /* C99 */
double round(double x); /* C99 */
long double roundl(long double x); /* C99 */
long int lroundf(float x); /* C99 */
long int lround(double x); /* C99 */
long int lroundl(long double x); /* C99 */
long long int llroundf(float x); /* C99 */
long long int llround(double x); /* C99 */
long long int llroundl(long double x); /* C99 */
trunc 函数trunc
函数将其参数舍入为浮动点格式的整数值,该整数值与参数最接近但其大小不超过参数的绝对值。
#include <math.h>
float truncf(float x); /* C99 */
double trunc(double x); /* C99 */
long double truncl(long double x); /* C99 */
remainder 函数remainder
函数根据 IEC 60559 定义计算余数 x REM y
。定义为“当 y ≠ 0
时,余数 r = x REM y
无论舍入模式如何,都通过数学减法公式 r = x - n * y
得出,其中 n
是 x/y
的最接近整数值;每当 |n - x/y| = ½
时,n
为偶数。因此,余数始终是精确的。如果 r = 0
,其符号与 x
相同。”这一定义适用于所有实现。
#include <math.h>
float remainderf(float x, float y); /* C99 */
double remainder(double x, double y); /* C99 */
long double remainderl(long double x, long double y); /* C99 */
remquo 函数remquo
函数返回与 remainder
函数相同的余数。在由 quo
指向的对象中,它存储一个值,其符号与 x/y
的符号相同,其大小在模 2n
下与 x/y
的整数商的大小同余,其中 n
是一个实现定义的整数,且 n ≥ 3
。
#include <math.h>
float remquof(float x, float y, int *quo); /* C99 */
double remquo(double x, double y, int *quo); /* C99 */
long double remquol(long double x, long double y, int *quo); /* C99 */
误差和伽马函数
erf
函数计算参数的误差函数:
2π∫0xe−t2dt\frac{2}{\sqrt{\pi}} \int_0^x e^{-t^2} dt
erfc
函数计算参数的补充误差函数(即 1 - erf(x)
)。如果参数太大,可能会发生范围错误。
lgamma
函数计算参数的伽马函数的绝对值的自然对数(即 loge|Γ(x)|
)。如果参数是负整数或零,可能会发生范围错误。
tgamma
函数计算参数的伽马函数(即 Γ(x)
)。如果参数是负整数,或者当参数为零时结果无法表示,则会发生域错误。也可能会发生范围错误。
#include <math.h>
float erff(float x); /* C99 */
double erf(double x); /* C99 */
long double erfl(long double x); /* C99 */
float erfcf(float x); /* C99 */
double erfc(double x); /* C99 */
long double erfcl(long double x); /* C99 */
float lgammaf(float x); /* C99 */
double lgamma(double x); /* C99 */
long double lgammal(long double x); /* C99 */
float tgammaf(float x); /* C99 */
double tgamma(double x); /* C99 */
long double tgammal(long double x); /* C99 */