首页 > 编程 > .NET > 正文

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

2024-07-10 13:27:18
字体:
来源:转载
供稿:网友

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

 

[cpp] view plaincopy
  1. /* ISO C99 7.6: 浮点数环境   <fenv.h> */  
  2. #ifndef _FENV_H  
  3. #define _FENV_H 1  
  4. #include <features.h>  
  5. /* 获取依赖于体系结构的定义。应该要有下面这些定义: 
  6.    fenv_t   表示浮点数环境的类型 
  7.    FE_DFL_ENV   fenv_t *指针类型的值,表示浮点数环境的缺省值(用于需要fenv_t参数的函数中) 
  8.    fexcept_t    表示浮点数异常标志(这个标志保存了浮点数的状态)的类型 
  9.     下面的宏被定义,当且仅当实现支持这些宏时: 
  10.    FE_INEXACT       不精确的结果 
  11.    FE_DIVBYZERO     除数为0 
  12.    FE_UNDERFLOW     结果向下溢出 
  13.    FE_OVERFLOW      结果向上溢出 
  14.    FE_INVALID       无效的运算 
  15.    FE_ALL_EXCEPT    所有被支持的异常的按位或 
  16.    下面这些宏被定义,当且仅当实现支持某些适当的舍入方式时: 
  17.    FE_TONEAREST     最近舍入 
  18.    FE_UPWARD        向正无穷大(+Inf)舍入 
  19.    FE_DOWNWARD      向负无穷大(-Inf)舍入 
  20.    FE_TOWARDZERO    向0舍入 
  21. */  
  22. #include <bits/fenv.h>  
  23. __BEGIN_DECLS  
  24. /* 浮点数异常处理  */  
  25. /* 清除EXCEPTS表示的异常状态  */  
  26. extern int feclearexcept (int __excepts) __THROW;  
  27. /* 将实现定义的异常标志EXCEPTS保存到FLAGP所指的对象中 */  
  28. extern int fegetexceptflag (fexcept_t *__flagp, int __excepts) __THROW;  
  29. /* 产生由EXCEPTS表示的异常 */  
  30. extern int feraiseexcept (int __excepts) __THROW;  
  31. /* 根把EXCEPTS异常标志设置为FLAGP所指对象中的值 */  
  32. extern int fesetexceptflag (__const fexcept_t *__flagp, int __excepts) __THROW;  
  33. /* 确定EXCEPTS指示的各个异常中有哪个异常子集被设置了 */  
  34. extern int fetestexcept (int __excepts) __THROW;  
  35. /* 舍入控制  */  
  36. /* 获得当前的舍入方向,表示为一个舍入方向宏值  */  
  37. extern int fegetround (void) __THROW;  
  38. /* 设置舍入方向,成功时返回0  */  
  39. extern int fesetround (int __rounding_direction) __THROW;  
  40. /* 浮点数环境  */  
  41. /* 获取当前的浮点数环境,并保存在ENVP所指的对象中 */  
  42. extern int fegetenv (fenv_t *__envp) __THROW;  
  43. /* 将当前浮点数环境保存到ENVP所指对象中,清除异常标志,然后安装一个不停止的浮点数环境(即 
  44.     忽略所有浮点数异常) */  
  45. extern int feholdexcept (fenv_t *__envp) __THROW;  
  46. /* 设置浮点数环境 */  
  47. extern int fesetenv (__const fenv_t *__envp) __THROW;  
  48. /* 将当前浮点数环境保存到一个临时存储中,然后安装ENVP所指的环境,并根据保存的异常环境 
  49.     产生异常 */  
  50. extern int feupdateenv (__const fenv_t *__envp) __THROW;  
  51.   
  52. /* 下面包含了一些作为扩展的优化函数  */  
  53. #ifdef __OPTIMIZE__  
  54. # include <bits/fenvinline.h>  
  55. #endif  
  56. #ifdef __USE_GNU  
  57. /* 激活个别的异常,激活的个数不会多于EXCEPTS指示的异常个数。如果所有异常被成功设置,则返回 
  58.     以前激活的异常,否则返回-1 */  
  59. extern int feenableexcept (int __excepts) __THROW;  
  60. /* 禁用个别的异常,禁用的异常个数不会多于EXCEPTS指示的异常个数。如果所有异常被成功禁用, 
  61.     则返回先前激活的异常,否则返回-1 */  
  62. extern int fedisableexcept (int __excepts) __THROW;  
  63. /* 返回激活的异常  */  
  64. extern int fegetexcept (void) __THROW;  
  65. #endif  
  66. __END_DECLS  
  67. #endif /* fenv.h */  

 

    bits/fenv.h内容如下:

 

[cpp] view plaincopy
  1. /* bits/fenv.h:定义了浮点数异常码,x86体系结构的版本(Linux下)  */  
  2. #ifndef _FENV_H  
  3. # error "Never use <bits/fenv.h> directly; include <fenv.h> instead."  
  4. #endif  
  5. #include <bits/wordsize.h>  
  6. /* 定义代表异常的位,我们使用FPU控制字中适当的位 */  
  7. enum  
  8.   {  
  9.     FE_INVALID = 0x01,           /* 无效的运算 */  
  10. #define FE_INVALID  FE_INVALID  
  11.     __FE_DENORM = 0x02,  
  12.     FE_DIVBYZERO = 0x04,        /* 除数为0 */  
  13. #define FE_DIVBYZERO    FE_DIVBYZERO  
  14.     FE_OVERFLOW = 0x08,        /* 结果向上溢出 */  
  15. #define FE_OVERFLOW FE_OVERFLOW  
  16.     FE_UNDERFLOW = 0x10,       /* 结果向下溢出 */  
  17. #define FE_UNDERFLOW    FE_UNDERFLOW  
  18.     FE_INEXACT = 0x20          /* 不精确的结果 */  
  19. #define FE_INEXACT  FE_INEXACT  
  20.   };  
  21. /* 所有异常的按位或 */  
  22. #define FE_ALL_EXCEPT /  
  23.     (FE_INEXACT | FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID)  
  24. /* ix87 FPU支持所有四种已经定义的舍入模式。我们又使用FPU控制字中位作为相应宏的值 */  
  25. enum  
  26.   {  
  27.     FE_TONEAREST = 0,         /* 最近舍入 */  
  28. #define FE_TONEAREST    FE_TONEAREST  
  29.     FE_DOWNWARD = 0x400,  
  30. #define FE_DOWNWARD FE_DOWNWARD  
  31.     FE_UPWARD = 0x800,       /* 向负无穷舍入 */  
  32. #define FE_UPWARD   FE_UPWARD  
  33.     FE_TOWARDZERO = 0xc00    /* 向正无穷舍入 */  
  34. #define FE_TOWARDZERO   FE_TOWARDZERO  
  35.   };  
  36.   
  37. /* 代表异常标志的类型  */  
  38. typedef unsigned short int fexcept_t;  
  39. /* 代表浮点数环境的类型。这个与块的布局有关的结构由fstenv指令写入,并且有另外的域作为 
  40.    MXCSR寄存器的内容,这个寄存器由stmxcsr指令写入 */  
  41. typedef struct  
  42.   {  
  43.     unsigned short int __control_word;  
  44.     unsigned short int __unused1;  
  45.     unsigned short int __status_word;  
  46.     unsigned short int __unused2;  
  47.     unsigned short int __tags;  
  48.     unsigned short int __unused3;  
  49.     unsigned int __eip;  
  50.     unsigned short int __cs_selector;  
  51.     unsigned int __opcode:11;  
  52.     unsigned int __unused4:5;  
  53.     unsigned int __data_offset;  
  54.     unsigned short int __data_selector;  
  55.     unsigned short int __unused5;  
  56. #if __WORDSIZE == 64  
  57.     unsigned int __mxcsr;  
  58. #endif  
  59.   }  
  60. fenv_t;  
  61. /* 缺省的浮点数环境:使用下面这个值  */  
  62. #define FE_DFL_ENV  ((__const fenv_t *) -1)  
  63. #ifdef __USE_GNU  
  64. /* 没有任何异常被屏蔽的浮点数环境  */  
  65. # define FE_NOMASK_ENV  ((__const fenv_t *) -2)  
  66. #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体系结构有全面的了解。由于代码依赖于体系结构,也没有特别值得研究的独立于体系结构的算法,因此这里并不打算解剖它们的源代码。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表