C标准库源码解剖(12):浮点数环境fenv.h

     为了编写高精度浮点数的运算,编程人员需要控制浮点数环境的各个方面:结果如何舍入,浮点数表达式如何简化与变换,如何处理浮点数异常(如下溢之类的浮点数异常是忽略还是产生错误),等等。C99引入了fenv.h来控制浮点数环境。
    1、fenv.h: 定义了浮点数环境控制函数、异常控制函数、舍入方式控制函数、浮点数异常码和舍入方式等。注意浮点数环境的实现是依赖于体系结构的,因为不同的体系结构有不同的浮点数指令集。依赖于体系结构的定义放在bits/fenv.h中,这里是x86体系结构的版本(Linux下)。
    标准头文件fenv.h内容如下:

/* ISO C99 7.6: 浮点数环境 <fenv.h> */ #ifndef _FENV_H #define _FENV_H 1 #include <features.h> /* 获取依赖于体系结构的定义。应该要有下面这些定义: fenv_t 表示浮点数环境的类型 FE_DFL_ENV fenv_t *指针类型的值,表示浮点数环境的缺省值(用于需要fenv_t参数的函数中) fexcept_t 表示浮点数异常标志(这个标志保存了浮点数的状态)的类型 下面的宏被定义,当且仅当实现支持这些宏时: FE_INEXACT 不精确的结果 FE_DIVBYZERO 除数为0 FE_UNDERFLOW 结果向下溢出 FE_OVERFLOW 结果向上溢出 FE_INVALID 无效的运算 FE_ALL_EXCEPT 所有被支持的异常的按位或 下面这些宏被定义,当且仅当实现支持某些适当的舍入方式时: FE_TONEAREST 最近舍入 FE_UPWARD 向正无穷大(+Inf)舍入 FE_DOWNWARD 向负无穷大(-Inf)舍入 FE_TOWARDZERO 向0舍入 */ #include <bits/fenv.h> __BEGIN_DECLS /* 浮点数异常处理 */ /* 清除EXCEPTS表示的异常状态 */ extern int feclearexcept (int __excepts) __THROW; /* 将实现定义的异常标志EXCEPTS保存到FLAGP所指的对象中 */ extern int fegetexceptflag (fexcept_t *__flagp, int __excepts) __THROW; /* 产生由EXCEPTS表示的异常 */ extern int feraiseexcept (int __excepts) __THROW; /* 根把EXCEPTS异常标志设置为FLAGP所指对象中的值 */ extern int fesetexceptflag (__const fexcept_t *__flagp, int __excepts) __THROW; /* 确定EXCEPTS指示的各个异常中有哪个异常子集被设置了 */ extern int fetestexcept (int __excepts) __THROW; /* 舍入控制 */ /* 获得当前的舍入方向,表示为一个舍入方向宏值 */ extern int fegetround (void) __THROW; /* 设置舍入方向,成功时返回0 */ extern int fesetround (int __rounding_direction) __THROW; /* 浮点数环境 */ /* 获取当前的浮点数环境,并保存在ENVP所指的对象中 */ extern int fegetenv (fenv_t *__envp) __THROW; /* 将当前浮点数环境保存到ENVP所指对象中,清除异常标志,然后安装一个不停止的浮点数环境(即 忽略所有浮点数异常) */ extern int feholdexcept (fenv_t *__envp) __THROW; /* 设置浮点数环境 */ extern int fesetenv (__const fenv_t *__envp) __THROW; /* 将当前浮点数环境保存到一个临时存储中,然后安装ENVP所指的环境,并根据保存的异常环境 产生异常 */ extern int feupdateenv (__const fenv_t *__envp) __THROW; /* 下面包含了一些作为扩展的优化函数 */ #ifdef __OPTIMIZE__ # include <bits/fenvinline.h> #endif #ifdef __USE_GNU /* 激活个别的异常,激活的个数不会多于EXCEPTS指示的异常个数。如果所有异常被成功设置,则返回 以前激活的异常,否则返回-1 */ extern int feenableexcept (int __excepts) __THROW; /* 禁用个别的异常,禁用的异常个数不会多于EXCEPTS指示的异常个数。如果所有异常被成功禁用, 则返回先前激活的异常,否则返回-1 */ extern int fedisableexcept (int __excepts) __THROW; /* 返回激活的异常 */ extern int fegetexcept (void) __THROW; #endif __END_DECLS #endif /* fenv.h */

    bits/fenv.h内容如下:

/* bits/fenv.h:定义了浮点数异常码,x86体系结构的版本(Linux下) */ #ifndef _FENV_H # error "Never use <bits/fenv.h> directly; include <fenv.h> instead." #endif #include <bits/wordsize.h> /* 定义代表异常的位,我们使用FPU控制字中适当的位 */ enum { FE_INVALID = 0x01, /* 无效的运算 */ #define FE_INVALID FE_INVALID __FE_DENORM = 0x02, FE_DIVBYZERO = 0x04, /* 除数为0 */ #define FE_DIVBYZERO FE_DIVBYZERO FE_OVERFLOW = 0x08, /* 结果向上溢出 */ #define FE_OVERFLOW FE_OVERFLOW FE_UNDERFLOW = 0x10, /* 结果向下溢出 */ #define FE_UNDERFLOW FE_UNDERFLOW FE_INEXACT = 0x20 /* 不精确的结果 */ #define FE_INEXACT FE_INEXACT }; /* 所有异常的按位或 */ #define FE_ALL_EXCEPT / (FE_INEXACT | FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID) /* ix87 FPU支持所有四种已经定义的舍入模式。我们又使用FPU控制字中位作为相应宏的值 */ enum { FE_TONEAREST = 0, /* 最近舍入 */ #define FE_TONEAREST FE_TONEAREST FE_DOWNWARD = 0x400, #define FE_DOWNWARD FE_DOWNWARD FE_UPWARD = 0x800, /* 向负无穷舍入 */ #define FE_UPWARD FE_UPWARD FE_TOWARDZERO = 0xc00 /* 向正无穷舍入 */ #define FE_TOWARDZERO FE_TOWARDZERO }; /* 代表异常标志的类型 */ typedef unsigned short int fexcept_t; /* 代表浮点数环境的类型。这个与块的布局有关的结构由fstenv指令写入,并且有另外的域作为 MXCSR寄存器的内容,这个寄存器由stmxcsr指令写入 */ typedef struct { unsigned short int __control_word; unsigned short int __unused1; unsigned short int __status_word; unsigned short int __unused2; unsigned short int __tags; unsigned short int __unused3; unsigned int __eip; unsigned short int __cs_selector; unsigned int __opcode:11; unsigned int __unused4:5; unsigned int __data_offset; unsigned short int __data_selector; unsigned short int __unused5; #if __WORDSIZE == 64 unsigned int __mxcsr; #endif } fenv_t; /* 缺省的浮点数环境:使用下面这个值 */ #define FE_DFL_ENV ((__const fenv_t *) -1) #ifdef __USE_GNU /* 没有任何异常被屏蔽的浮点数环境 */ # define FE_NOMASK_ENV ((__const fenv_t *) -2) #endif

    标准C中指定的接口有:
    (1)浮点数环境类型:fenv_t,是一个结构体,里面的成员依赖于体系结构。
    (2)浮点数环境的缺省值:FE_DFL_ENV,是一个fenv_t *指针类型的值(为-1),用于需要fenv_t参数的函数中。
    (3)浮点数异常标志类型:fexcept_t,为unsigned short类型。异常标志中保存了浮点数的状态。
    (4)浮点数异常:FE_INEXACT, FE_DIVBYZERO, FE_UNDERFLOW, FE_OVERFLOW, FE_INVALID, FE_ALL_EXCEPT。
    (5)浮点数舍入方式:FE_TONEAREST, FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO。
    (6)浮点数环境控制函数:fegetenv, fesetenv, feholdexcpet, feupdateenv。
    (7)浮点数异常处理函数:fegetexceptflag, fesetexceptflag, fetestexcept, feraiseexcept, feclearexcept。
    (8)浮点数舍入控制函数:fegetround, fesetround。
    2、浮点数环境、异常、舍入控制函数的实现: 这些函数的实现都是依赖于体系结构的,x86体系结构的部分在glibc源码的sysdeps/i386/fpu目录下。每个函数对应一个C语言源文件,由于要用到浮点数操作指令,因此都嵌入了汇编代码。这些函数的功能都非常简单,主要是通过fenv_t的实例或fexcept_t的实例来获取或设置相应的值,因此代码都不长,要理解这些代码,主要是要对x86体系结构有全面的了解。由于代码依赖于体系结构,也没有特别值得研究的独立于体系结构的算法,因此这里并不打算解剖它们的源代码。

你可能感兴趣的:(C标准库源码解剖(12):浮点数环境fenv.h)