文章目录
1. 前言
The GNU C Library Reference Manual for version 2.35
2. 算术函数
Arithmetic Functions
本章包含有关执行基本算术运算的函数的信息,例如将浮点数拆分为整数和小数部分或检索复数的虚数部分。这些函数在头文件 math.h 和 complex.h 中声明。
2.1. 整数
Integers
C 语言定义了几种整数数据类型:整数、短整数、长整数和字符,所有这些数据类型都有有符号和无符号两种。GNU C 编译器也扩展了该语言以包含 long long 整数。
C 整数类型旨在允许代码在具有不同固有数据大小(字长)的机器之间移植,因此每种类型在不同机器上可能具有不同的范围。这样做的问题是,通常需要为特定范围的整数编写程序,并且有时必须针对特定大小的存储空间编写程序,而不管程序在哪台机器上运行。
为了解决这个问题,GNU C 库包含 C 类型定义,您可以使用它来声明满足您确切需要的整数。因为 GNU C 库头文件是针对特定机器定制的,所以您的程序源代码不必如此。
这些 typedef 在 stdint.h 中。
如果您要求整数精确地用 N 位表示,请使用以下类型之一,并明显映射到位大小和符号:
- int8_t
- int16_t
- int32_t
- int64_t
- uint8_t
- uint16_t
- uint32_t
- uint64_t
如果您的 C 编译器和目标机器不允许特定大小的整数,则对应的上述类型不存在。
如果您不需要特定的存储大小,但想要具有至少 N 位的最小数据结构,请使用以下之一:
- int_least8_t
- int_least16_t
- int_least32_t
- int_least64_t
- uint_least8_t
- uint_least16_t
- uint_least32_t
- uint_least64_t
如果您不需要特定的存储大小,但想要允许最快访问同时至少具有 N 位的数据结构(并且在具有相同访问速度的数据结构中,最小的一个),请使用以下之一:
- int_fast8_t
- int_fast16_t
- int_fast32_t
- int_fast64_t
- uint_fast8_t
- uint_fast16_t
- uint_fast32_t
- uint_fast64_t
如果您想要一个在使用它的平台上具有最大范围的整数,请使用以下选项之一。如果你使用这些,你应该编写考虑到变量大小和整数范围的代码。
- intmax_t
- uintmax_t
GNU C 库还提供了告诉您每种整数数据类型的最大和最小可能值的宏。宏名称遵循以下示例:INT32_MAX、UINT8_MAX、INT_FAST32_MIN、INT_LEAST64_MIN、UINTMAX_MAX、INTMAX_MAX、INTMAX_MIN。请注意,无符号整数最小值没有宏。这些总是零。同样,这些类型的宽度也有 INTMAX_WIDTH 等宏。这些整数类型宽度的宏来自 TS 18661-1:2014。
有类似的宏可用于 C 的内置整数类型,这些宏应该随 C 编译器一起提供。这些在数据类型测量中进行了描述。
不要忘记,您可以将 C sizeof 函数与这些数据类型中的任何一种一起使用,以获取每种使用的存储字节数。
2.2. 整数除法
Integer Division
本节介绍执行整数除法的函数。当使用 GNU CC 时,这些函数是多余的,因为在 GNU C 中,“/”运算符总是向零舍入。但在其他 C 实现中,“/”可能会以不同的方式舍入否定参数。div 和 ldiv 很有用,因为它们指定如何将商:向零舍入。余数与分子的符号相同。
这些函数被指定返回一个结果 r,使得值 r.quot*denominator + r.rem 等于分子。
要使用这些工具,您应该在程序中包含头文件 stdlib.h。
数据类型:div_t
这是一种结构类型,用于保存 div 函数返回的结果。它有以下成员:
int quot
除法的商。
int rem
除法的余数。
函数:div_t div (int numerator, int denominator)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
函数 div 计算分子除以分母的商和余数,并以 div_t 类型的结构返回结果。
如果结果无法表示(如除以零),则行为未定义。
这是一个示例,尽管不是很有用。
div_t result;
result = div (20, -6);
现在 result.quot 是 -3 而 result.rem 是 2。
数据类型:ldiv_t
这是一种结构类型,用于保存 ldiv 函数返回的结果。它有以下成员:
long int quot
除法的商。
long int rem
除法的余数。
(这与 div_t 相同,只是组件的类型为 long int 而不是 int。)
函数:ldiv_t ldiv (long int numerator, long int denominator)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
ldiv 函数与 div 类似,不同之处在于参数是 long int 类型,并且结果作为 ldiv_t 类型的结构返回。
数据类型:lldiv_t
这是一种结构类型,用于保存 lldiv 函数返回的结果。它有以下成员:
long long int quot
除法的商。
long long int rem
除法的余数。
(这与 div_t 相同,只是组件的类型是 long long int 而不是 int。)
函数:lldiv_t lldiv (long long int numerator, long long int denominator)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
lldiv 函数类似于 div 函数,但参数是 long long int 类型,结果作为 lldiv_t 类型的结构返回。
lldiv 函数是在 ISO C99 中添加的。
数据类型:imaxdiv_t
这是一种结构类型,用于保存 imaxdiv 函数返回的结果。它有以下成员:
intmax_t quot
除法的商。
intmax_t rem
除法的余数。
(这与 div_t 相同,只是组件的类型为 intmax_t 而不是 int。)
有关 intmax_t 类型的描述,请参见整数。
函数:imaxdiv_t imaxdiv (intmax_t numerator, intmax_t denominator)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
imaxdiv 函数类似于 div 函数,但参数是 intmax_t 类型,结果作为 imaxdiv_t 类型的结构返回。
有关 intmax_t 类型的描述,请参见整数。
在 ISO C99 中添加了 imaxdiv 函数。
2.3. 浮点数
Floating Point Numbers
大多数计算机硬件都支持两种不同的数字:整数(…-3、-2、-1、0、1、2、3…)和浮点数。浮点数由三部分组成:尾数、指数和符号位。由浮点值表示的实数由 (s ? -1 : 1) · 2^e · M 给出,其中 s 是符号位,e 是指数,M 是尾数。有关详细信息,请参阅浮点表示概念。(指数可能有不同的基数,但所有现代硬件都使用 2。)
浮点数可以表示实数的有限子集。虽然这个子集对于大多数用途来说足够大,但重要的是要记住,唯一可以精确表示的实数是有理数,其终止二进制扩展比尾数的宽度短。即使是简单的分数,例如 1/5,也只能用浮点数来近似。
数学运算和函数经常需要产生不可表示的值。出于实际目的,这些值通常可以足够接近,但有时不能。从历史上看,没有办法判断计算结果何时不准确。现代计算机执行 IEEE 754 数值计算标准,该标准定义了一个框架,用于在计算结果不可信时向程序指示。该框架由一组异常组成,这些异常指示无法表示结果的原因,以及特殊值无穷大而不是数字 (NaN)。
2.4. 浮点数分类函数
Floating-Point Number Classification Functions
ISO C99 定义了允许您确定变量所包含的浮点数类型的宏。
宏:int fpclassify (float-type x)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
这是一个通用宏,适用于所有浮点类型,并返回一个 int 类型的值。可能的值是:
FP_NAN
浮点数 x 是“非数字”(参见 Infinity 和 NaN)
FP_INFINITE
x 的值是正无穷大或负无穷大(参见 Infinity 和 NaN)
FP_ZERO
x 的值为零。在 IEEE 754 等浮点格式中,可以对零进行符号化,如果 x 为负零,也会返回此值。
FP_SUBNORMAL
绝对值太小而无法以正常格式表示的数字以替代的非规范化格式表示(请参阅浮点表示概念)。这种格式不太精确,但可以表示接近零的值。fpclassify 以这种替代格式为 x 的值返回该值。
FP_NORMAL
为 x 的所有其他值返回此值。它表明该数字没有什么特别之处。
如果必须测试一个数字的多个属性,则 fpclassify 最有用。还有更具体的宏一次只测试一个属性。通常这些宏比 fpclassify 执行得更快,因为它们有特殊的硬件支持。因此,您应该尽可能使用特定的宏。
宏:int iscanonical (float-type x)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
在某些浮点格式中,某些值具有规范(首选)和非规范编码(对于 IEEE 交换二进制格式,所有编码都是规范的)。如果 x 具有规范编码,则此宏返回非零值。它来自 TS 18661-1:2014。
请注意,某些格式具有多个值的编码,这些编码都同样规范;iscanonical 为所有此类编码返回一个非零值。此外,格式可能具有不对应于任何有效类型值的编码。在 ISO C 术语中,这些是陷阱表示;在 GNU C 库中,iscanonical 对此类编码返回零。
宏:int isfinite (float-type x)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
如果 x 是有限的,则此宏返回一个非零值:不是正无穷大或负无穷大,也不是 NaN。它相当于
(fpclassify (x) != FP_NAN && fpclassify (x) != FP_INFINITE)
isfinite 被实现为一个接受任何浮点类型的宏。
宏:int isnormal (float-type x)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
如果 x 是有限且归一化的,则此宏返回一个非零值。它相当于
(fpclassify (x) == FP_NORMAL)
宏:int isnan (float-type x)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
如果 x 为 NaN,则此宏返回非零值。它相当于
(fpclassify (x) == FP_NAN)
宏:int issignaling (float-type x)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
如果 x 是信号 NaN (sNaN),则此宏返回非零值。它来自 TS 18661-1:2014。
宏:int issubnormal (float-type x)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
如果 x 是次正规的,则此宏返回一个非零值。它来自 TS 18661-1:2014。
宏: int iszero (float-type x)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
如果 x 为零,此宏将返回一个非零值。它来自 TS 18661-1:2014。
BSD 提供了另一组浮点分类函数。GNU C 库也支持这些函数;但是,我们建议您在新代码中使用 ISO C99 宏。这些是标准的,将更广泛地使用。此外,由于它们是宏,因此您不必担心它们的参数类型。
函数:
int isinf (double x)
int isinff (float x)
int isinfl (long double x)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
如果 x 表示负无穷大,此函数返回 -1,如果 x 表示正无穷大,则返回 1,否则返回 0。
函数:
int isnan (double x)
int isnanf (float x)
int isnanl (long double x)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
如果 x 是“非数字”值,则此函数返回非零值,否则返回零。
注意:ISO C99 定义的 isnan 宏会覆盖 BSD 函数。这通常不是问题,因为这两个例程的行为相同。但是,如果你真的因为某种原因需要获取 BSD 功能,你可以写
(isnan) (x)
函数:
int finite (double x)
int finitef (float x)
int finitel (long double x)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
如果 x 既不是无穷大也不是“非数字”值,则此函数返回一个非零值,否则返回零。
可移植性说明:本节列出的功能是 BSD 扩展。
2.5. 浮点计算中的错误
Errors in Floating-Point Calculations
2.5.1. FP 异常
FP Exceptions
IEEE 754 标准定义了计算期间可能发生的五种异常。每个都对应于一种特定类型的错误,例如溢出。
当异常发生时(当引发异常时,在标准的语言中),可能会发生以下两种情况之一。默认情况下,异常只是简单地在浮点状态字中注明,程序继续进行,就好像什么都没发生一样。该操作会产生一个默认值,该值取决于异常(见下表)。您的程序可以检查状态字以找出发生了哪些异常。
或者,您可以启用异常陷阱。在这种情况下,当引发异常时,您的程序将收到 SIGFPE 信号。此信号的默认操作是终止程序。请参阅信号处理,了解如何更改信号的效果。
IEEE 754 中定义的例外是:
‘Invalid Operation’
如果给定的操作数对于要执行的操作无效,则会引发此异常。例如(参见 IEEE 754,第 7 节):
- 加法或减法:∞ - ∞。(但 ∞ + ∞ = ∞)。
- 乘法:0·∞。
- 除法:0/0 或 ∞/∞。
- 余数:x REM y,其中 y 为零或 x 为无穷大。
- 如果操作数小于零,则平方根。更一般地,任何在其域之外评估的数学函数都会产生此异常。
- 当数字无法以目标格式表示时(由于溢出、无穷大或 NaN),将浮点数转换为整数或十进制字符串。
- 无法识别的输入字符串的转换。
- 当一个或另一个操作数是 NaN 时,通过涉及 < 或 > 的谓词进行比较。您可以改用无序比较函数来防止此异常;请参阅浮点比较函数。
如果异常没有捕获,则运算结果为 NaN。
‘Division by Zero’
当有限的非零数除以零时会引发此异常。如果没有发生陷阱,则结果为 +∞ 或 -∞,具体取决于操作数的符号。
‘Overflow’
只要结果不能以目标的精度格式表示为有限值,就会引发此异常。如果没有发生陷阱,则结果取决于中间结果的符号和当前的舍入模式(IEEE 754,第 7.3 节):
- 四舍五入将所有溢出带到∞,中间结果的符号。
- 向 0 舍入将所有溢出带到带有中间结果符号的最大可表示有限数。
- 向 -∞ 舍入会将正溢出带到最大可表示的有限数,将负溢出带到 -∞。
- 向 ∞ 舍入会将负溢出带到最负的可表示有限数,将正溢出带到 ∞。
每当引发溢出异常时,也会引发不精确异常。
‘Underflow’
当中间结果太小而无法准确计算,或者四舍五入到目标精度的运算结果太小而无法归一化时,就会引发下溢异常。
当没有为下溢异常安装陷阱时,只有在检测到微小和精度损失时才会发出下溢信号(通过下溢标志)。如果未安装陷阱处理程序,则操作以不精确的小值继续,如果目标精度不能保持小的精确结果,则操作为零。
‘Inexact’
如果舍入结果不准确(例如在计算 2 的平方根时)或结果溢出而没有溢出陷阱,则会发出此异常信号。
2.5.2. 无穷大和NaN
Infinity and NaN
IEEE 754 浮点数可以表示正无穷或负无穷,以及 NaN(不是数字)。这三个值来自结果未定义或无法准确表示的计算。您还可以故意为它们中的任何一个设置一个浮点变量,这有时很有用。产生无穷大或 NaN 的一些计算示例:
1/0 = ∞
log (0) = -∞
sqrt (-1) = NaN
当计算产生任何这些值时,也会发生异常;请参阅 FP 例外。
基本运算和数学函数都接受无穷大和 NaN 并产生合理的输出。正如人们所期望的那样,无穷大通过计算传播:例如,2 + ∞ = ∞,4/∞ = 0,atan (∞) = π/2。另一方面,NaN 会感染任何涉及它的计算。除非无论用什么实际值替换 NaN,计算都会产生相同的结果,否则结果就是 NaN。
在比较运算中,正无穷大于除自身和 NaN 之外的所有值,负无穷小于除自身和 NaN 之外的所有值。NaN 是无序的:它不等于、大于或小于任何东西,包括它自己。如果 x 的值为 NaN,则 x == x 为假。您可以使用它来测试一个值是否为 NaN,但测试 NaN 的推荐方法是使用 isnan 函数(请参阅浮点数分类函数)。此外,<、>、<= 和 >= 在应用于 NaN 时会引发异常。
math.h 定义了允许您将变量显式设置为无穷大或 NaN 的宏。
宏:float INFINITY
表示正无穷大的表达式。它等于 1.0 / 0.0 等数学运算产生的值。-INFINITY 表示负无穷大。
您可以通过将浮点值与此宏进行比较来测试它是否是无限的。但是,不建议这样做;您应该改用 isfinite 宏。请参阅浮点数分类函数。
这个宏是在 ISO C99 标准中引入的。
宏:float NAN
表示“非数字”值的表达式。这个宏是一个 GNU 扩展,仅在支持“非数字”值的机器上可用——也就是说,在所有支持 IEEE 浮点的机器上。
你可以使用‘#ifdef NAN’来测试机器是否支持NaN。(当然,您必须安排 GNU 扩展可见,例如通过定义 _GNU_SOURCE,然后您必须包含 math.h。)
宏:float SNANF
宏:double SNAN
宏:long double SNANL
宏:_FloatN SNANFN
宏:_FloatNx SNANFNx
这些由 TS 18661-1:2014 和 TS 18661-3:2015 定义的宏是用于信号 NaN 的常量表达式。
宏:int FE_SNANS_ALWAYS_SIGNAL
此宏由 TS 18661-1:2014 定义,在 fenv.h 中定义为 1,以指示具有信号 NaN 输入和浮点结果的函数和操作始终引发无效异常并返回安静的 NaN,即使在情况下 (例如 fmax、hypot 和 pow),其中安静的 NaN 输入可以产生非 NaN 结果。由于某些编译器优化可能无法正确处理信号 NaN,因此仅在启用了对信号 NaN 的编译器支持时才定义此宏。可以使用 GCC 选项 -fsignaling-nans 启用该支持。
IEEE 754 还允许另一个不寻常的值:负零。当您将一个正数除以负无穷大时,或者当负数结果小于表示限制时,会产生此值。
2.5.3. 检查 FPU 状态字
Examining the FPU status word
ISO C99 定义了查询和操作浮点状态字的函数。您可以在方便时使用这些函数来检查未捕获的异常,而不是在计算过程中担心它们。
这些常量代表各种 IEEE 754 异常。并非所有 FPU 都报告所有不同的异常。当且仅当您为其编译的 FPU 支持该异常时,才定义每个常量,因此您可以使用“#ifdef”测试 FPU 支持。它们在 fenv.h 中定义。
FE_INEXACT
不准确的例外。
FE_DIVBYZERO
除以零异常。
FE_UNDERFLOW
下溢异常。
FE_OVERFLOW
溢出异常。
FE_INVALID
无效异常。
宏 FE_ALL_EXCEPT 是 FP 实现支持的所有异常宏的按位或。
这些函数允许您清除异常标记、测试异常以及保存和恢复标记的异常集。
函数:int feclearexcept (int excepts)
Preliminary: | MT-Safe | AS-Safe !posix | AC-Safe !posix | See POSIX Safety Concepts.
此函数清除由异常指示的所有受支持的异常标志。
如果操作成功,该函数返回零,否则返回非零值。
函数:int feraiseexcept (int excepts)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
此函数引发异常指示的受支持异常。如果在 excepts 中设置了多个异常位,则引发异常的顺序是未定义的,除非在不精确 (FE_INEXACT) 之前引发上溢 (FE_OVERFLOW) 或下溢 (FE_UNDERFLOW)。无论是上溢还是下溢,也会引发不精确的异常,这也取决于实现。
如果操作成功,该函数返回零,否则返回非零值。
函数:int fesetexcept (int excepts)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
此函数设置异常指示的受支持异常标志,例如 feraiseexcept,但不会导致启用陷阱。fesetexcept 来自 TS 18661-1:2014。
如果操作成功,该函数返回零,否则返回非零值。
函数:int fetestexcept (int excepts)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
测试当前是否设置了参数 except 所指示的异常标志。如果其中任何一个是,则返回一个非零值,该值指定设置了哪些异常。否则结果为零。
为了理解这些函数,假设状态字是一个名为 status 的整数变量。feclearexcept 等价于“status &= ~excepts”,而 fetestexcept 等价于“(status & excepts)”。当然,实际的实现可能会有很大的不同。
异常标志只有在程序明确请求时才会被清除,通过调用 feclearexcept。如果您想从一组计算中检查异常,您应该首先清除所有标志。下面是一个使用 fetestexcept 的简单示例:
{
double f;
int raised;
feclearexcept (FE_ALL_EXCEPT);
f = compute ();
raised = fetestexcept (FE_OVERFLOW | FE_INVALID);
if (raised & FE_OVERFLOW) {
/* … */ }
if (raised & FE_INVALID) {
/* … */ }
/* … */
}
您不能在状态字中显式设置位。但是,您可以保存整个状态字并在以后恢复它。这是通过以下功能完成的:
函数:int fegetexceptflag (fexcept_t *flagp, int excepts)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
此函数在 flagp 指向的变量中存储一个实现定义的值,该值表示由 excepts 指示的异常标志的当前设置。
如果操作成功,该函数返回零,否则返回非零值。
函数:int fesetexceptflag (const fexcept_t *flagp, int excepts)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
此函数将异常指示的异常标志恢复为存储在 flagp 指向的变量中的值。
如果操作成功,该函数返回零,否则返回非零值。
请注意,存储在 fexcept_t 中的值与 fetestexcept 返回的位掩码没有相似之处。该类型甚至可能不是整数。不要尝试修改 fexcept_t 变量。
函数:int fetestexceptflag (const fexcept_t *flagp, int excepts)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
测试 flagp 指向的变量中是否设置了参数 excepts 指示的异常标志。如果其中任何一个是,则返回一个非零值,该值指定设置了哪些异常。否则结果为零。fetestexceptflag 来自 TS 18661-1:2014。
2.5.4. 数学函数的错误报告
Error Reporting by Mathematical Functions
许多数学函数仅在实数或复数的子集上定义。即使它们是在数学上定义的,它们的结果也可能大于或小于它们的返回类型可表示的范围,而不会损失准确性。这些分别称为域错误、上溢和下溢。当这些错误之一发生时,数学函数会做几件事。在本手册中,我们将完整的响应称为域错误、上溢或下溢的信号。
当数学函数遇到域错误时,它会引发无效异常并返回 NaN。它还将 errno 设置为 EDOM;这是为了与不支持 IEEE 754 异常处理的旧系统兼容。同样,当发生溢出时,数学函数会引发溢出异常,并且在默认舍入模式下,酌情返回 ∞ 或 -∞(在其他舍入模式下,在适合该舍入模式时返回相应符号的最大有限值) .如果返回 ∞ 或 -∞,它们还将 errno 设置为 ERANGE;当溢出时返回有限值时,errno 可能会也可能不会设置为 ERANGE。当发生下溢时,将引发下溢异常,并根据函数的数学结果和舍入模式返回零(适当有符号)或次正规值。errno 可以设置为 ERANGE,但这不能保证;GNU C 库应该在下溢到适当的符号零时设置它,但不一定针对其他下溢。
当数学函数的参数是信号 NaN 时,GNU C 库不认为这是一个域错误,因此 errno 保持不变,但仍会引发无效异常(指定处理信号 NaN 的少数函数除外不同)。
一些数学函数在数学上定义为在其部分域上产生一个复数值。最熟悉的例子是取负数的平方根。在这种情况下,复杂的数学函数(例如 csqrt)将返回适当的复数值。实值函数(例如 sqrt)将发出域错误信号。
一些较旧的硬件不支持无穷大。在该硬件上,溢出会返回一个特定的非常大的数字(通常是最大的可表示数字)。math.h 定义了可用于测试新旧硬件溢出的宏。
宏:double HUGE_VAL
宏:float HUGE_VALF
宏:long double HUGE_VALL
宏:_FloatN HUGE_VAL_FN
宏:_FloatNx HUGE_VAL_FNx
表示特定非常大数字的表达式。在使用 IEEE 754 浮点格式的机器上,HUGE_VAL 是无穷大。在其他机器上,它通常是可以表示的最大正数。
当结果太大而无法表示时,数学函数会返回适当类型的 HUGE_VAL 或 -HUGE_VAL 版本。
2.6. 舍入模式
Rounding Modes
浮点计算在内部以额外的精度执行,然后四舍五入以适合目标类型。这可确保结果与输入数据一样精确。IEEE 754 定义了四种可能的舍入模式:
-
Round to nearest. 四舍五入到最近。
这是默认模式。除非对其他其中之一有特殊需要,否则应该使用它。在这种模式下,结果将四舍五入到最接近的可表示值。如果结果介于两个可表示值之间,则选择偶数可表示值。即使在这里也意味着最低位为零。这种舍入模式可防止统计偏差并保证数值稳定性:冗长计算中的舍入误差将保持小于 FLT_EPSILON 的一半。
-
Round toward plus Infinity. 向正无穷方向舍入。
所有结果都四舍五入到大于结果的最小可表示值。
-
Round toward minus Infinity. 向负无穷大舍入。
所有结果都四舍五入到小于结果的最大可表示值。
-
Round toward zero. 向零舍入。
所有结果都四舍五入到最大可表示值,其幅度小于结果的幅度。换句话说,如果结果为负,则向上取整;如果是正数,则向下舍入。
fenv.h 定义了可用于引用各种舍入模式的常量。当且仅当 FPU 支持相应的舍入模式时,才会定义每一个。
FE_TONEAREST
四舍五入到最近。
FE_UPWARD
向 +∞ 舍入。
FE_DOWNWARD
向 -∞ 舍入。
FE_TOWARDZERO
向零舍入。
下溢是一种不寻常的情况。通常,IEEE 754 浮点数总是被规范化(请参阅浮点表示概念)。小于 2^r 的数字(其中 r 是最小指数,FLT_MIN_RADIX-1 表示浮点数)不能表示为规范化数字。将所有这些数字四舍五入为零或 2^r 会导致某些算法在 0 处失败。因此,它们以非规范化形式保留。这会导致精度损失,因为尾数的某些位被盗以指示小数点。
如果结果太小而无法表示为非规范化数字,则将其四舍五入为零。但是,结果的符号被保留;如果计算为负,则结果为负零。负零也可能来自于无穷大的某些运算,例如 4/-∞。
在任何时候,都会选择上述四种舍入模式中的一种。您可以找出哪个具有此函数:
函数:int fegetround (void)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
返回当前选择的舍入模式,由定义的舍入模式宏的值之一表示。
要更改舍入模式,请使用此函数:
函数:int fesetround (int round)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
将当前选定的舍入模式更改为舍入。如果 round 不对应于支持的舍入模式之一,则不会更改任何内容。如果更改舍入模式,fesetround 返回零,如果不支持该模式,则返回非零值。
如果可能,您应该避免更改舍入模式。这可能是一项昂贵的操作;此外,某些硬件要求您以不同的方式编译程序以使其工作。生成的代码可能会运行得更慢。有关详细信息,请参阅您的编译器文档。
2.7. 浮点控制函数
Floating-Point Control Functions
IEEE 754 浮点实现允许程序员通过设置控制字中的位来决定每个异常是否会发生陷阱。在 C 中,陷阱导致程序接收到 SIGFPE 信号;请参阅信号处理。
NB:IEEE 754 表示陷阱处理程序被赋予了异常情况的详细信息,并且可以设置结果值。C 信号不提供任何机制来来回传递此信息。因此,在 C 中捕获异常并不是很有用。
在执行某些计算时,有时需要保存浮点单元的状态。该库提供了保存和恢复异常标志、生成陷阱的异常集和舍入模式的函数。此信息称为浮点环境。
保存和恢复浮点环境的函数都使用 fenv_t 类型的变量来存储信息。这种类型在 fenv.h 中定义。它的大小和内容是实现定义的。您不应尝试直接操作此类型的变量。
要保存 FPU 的状态,请使用以下函数之一:
函数:int fegetenv (fenv_t *envp)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
将浮点环境存储在 envp 指向的变量中。
如果操作成功,该函数返回零,否则返回非零值。
函数:int feholdexcept (fenv_t *envp)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
将当前浮点环境存储在 envp 指向的对象中。然后清除所有异常标志,并将 FPU 设置为不捕获异常。并非所有 FPU 都支持捕获无异常;如果 feholdexcept 无法设置此模式,则返回非零值。如果成功,则返回零。
恢复浮点环境的函数可以采用以下类型的参数:
指向 fenv_t 对象的指针,这些对象之前通过调用 fegetenv 或 feholdexcept 进行了初始化。
特殊宏 FE_DFL_ENV 表示程序启动时可用的浮点环境。
实现定义的宏名称以 FE_ 开头,类型为 fenv_t *。
如果可能,GNU C 库定义了一个宏 FE_NOMASK_ENV,它代表了一个环境,在该环境中,引发的每个异常都会导致陷阱发生。您可以使用#ifdef 测试此宏。只有在定义了 _GNU_SOURCE 时才定义它。
某些平台可能会定义其他预定义环境。
要设置浮点环境,您可以使用以下任一函数:
函数:int fesetenv (const fenv_t *envp)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
将浮点环境设置为 envp 所描述的环境。
如果操作成功,该函数返回零,否则返回非零值。
函数:int feupdateenv (const fenv_t *envp)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
与 fesetenv 一样,此函数将浮点环境设置为 envp 所描述的环境。但是,如果在调用 feupdateenv 之前在状态字中标记了任何异常,则在调用之后它们仍然被标记。也就是说,调用feupdateenv后,状态字就是前一个状态字和envp中保存的状态字的位或。
如果操作成功,该函数返回零,否则返回非零值。
TS 18661-1:2014 定义了额外的函数来保存和恢复浮点控制模式(例如舍入模式和是否启用陷阱),同时保持其他状态(例如引发的标志)不变。
特殊宏 FE_DFL_MODE 可以传递给 fesetmode。它代表程序启动时的浮点控制模式。
函数:int fegetmode (femode_t *modep)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
将浮点控制模式存储在 modep 指向的变量中。
如果操作成功,该函数返回零,否则返回非零值。
函数:int fesetmode (const femode_t *modep)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
将浮点控制模式设置为 modep 所描述的模式。
如果操作成功,该函数返回零,否则返回非零值。
如果引发异常会导致陷阱发生,要控制个别异常,可以使用以下两个函数。
可移植性注意:这些函数都是 GNU 扩展。
函数:int feenableexcept (int excepts)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
此函数为参数 excepts 指示的每个异常启用陷阱。检查 FPU 状态字中描述了各个异常。仅启用指定的异常,其他异常的状态不变。
如果操作成功,该函数返回先前启用的异常,否则返回-1。
函数:int fedisableexcept (int excepts)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
此函数禁用每个异常的陷阱,如参数 excepts 所示。检查 FPU 状态字中描述了各个异常。只有指定的异常被禁用,其他异常的状态不变。
如果操作成功,该函数返回先前启用的异常,否则返回-1。
函数:int fegetexcept (void)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
该函数返回所有当前启用的异常的位掩码。如果失败,它会返回 -1。
2.8. 算术函数
Arithmetic Functions
C 库提供了对浮点数进行基本操作的函数。这些包括绝对值、最大值和最小值、归一化、位旋转、舍入和其他一些。
2.8.1. 绝对值
Absolute Value
提供这些函数用于获取数字的绝对值(或大小)。如果 x 为正,则实数 x 的绝对值为 x,如果 x 为负,则为 -x。对于复数 z,其实部为 x,虚部为 y,其绝对值为 sqrt (xx + yy)。
abs、labs 和 llabs 的原型在 stdlib.h 中;imaxabs 在 inttypes.h 中声明;fabs 函数在 math.h 中声明;cabs 函数在 complex.h 中声明。
函数:
int abs (int number)
long int labs (long int number)
long long int llabs (long long int number)
intmax_t imaxabs (intmax_t number)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
这些函数返回数字的绝对值。
大多数计算机使用二进制补码整数表示,其中无法表示 INT_MIN(可能的最小 int)的绝对值;因此,没有定义 abs (INT_MIN)。
llabs 和 imaxdiv 是 ISO C99 的新功能。
有关 intmax_t 类型的描述,请参见整数。
函数:
double fabs (double number)
float fabsf (float number)
long double fabsl (long double number)
_FloatN fabsfN (_FloatN number)
_FloatNx fabsfNx (_FloatNx number)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
该函数返回浮点数数字的绝对值。
函数:
double cabs (complex double z)
float cabsf (complex float z)
long double cabsl (complex long double z)
_FloatN cabsfN (complex _FloatN z)
_FloatNx cabsfNx (complex _FloatNx z)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
这些函数返回复数 z 的绝对值(请参阅复数)。复数的绝对值为:
sqrt (creal (z) * creal (z) + cimag (z) * cimag (z))
应始终使用此函数而不是直接公式,因为它需要特别注意避免丢失精度。它还可以利用对该操作的硬件支持。请参阅幂和对数中的 hypot。
2.8.2. 归一化函数
Normalization Functions
本节中描述的函数主要是作为一种有效地对浮点数执行某些低级操作的方法,这些浮点数在内部使用二进制基数表示;请参阅浮点表示概念。即使表示不使用基数 2,这些函数也需要具有等效的行为,但当然它们在这些情况下不太可能特别有效。
所有这些函数都在 math.h 中声明。
函数:
double frexp (double value, int *exponent)
float frexpf (float value, int *exponent)
long double frexpl (long double value, int *exponent)
_FloatN

本文详细探讨了C语言中的算术函数,特别是整数数据类型、整数除法操作及其实现细节。介绍了GCC提供的整数类型定义以确保代码的可移植性,以及div函数如何进行整数除法并处理商与余数。同时概述了浮点数分类宏,如fpclassify,用于判断浮点数的特性。
最低0.47元/天 解锁文章
2632

被折叠的 条评论
为什么被折叠?



