C++学习------cfenv头文件的作用与源码分析01

367 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第14天,点击查看活动详情

引言

cfenv是C++对C语言头文件fenv.h的封装,该头文件定义了一系列与浮点数运算环境相关的函数和宏定义,以及一些相关的结构体定义。它的作用主要是控制程序运行过程中浮点数运算的状态flag和控制模式,接下来我们来看看这个头文件的具体作用与实现原理。

注一:下面的源码参考android-12.0.0_r3中的源码,具体代码路径为:

www.aospxref.com/android-12.…

注二:贴近底层的代码实现通常都与具体的计算机体系结构相关,如:aarch64、arm、i386、x86_64等,文中以笔者的运行环境为主,主要讲述x86_64架构相关的代码实现与分析。

C++的封装逻辑

参考对应文件www.aospxref.com/android-12.…中的代码片段如下:

56 #include <__config>
57 #include <fenv.h>
58 
59 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
60 #pragma GCC system_header
61 #endif
62 
63 _LIBCPP_BEGIN_NAMESPACE_STD
64 
65 using ::fenv_t;
66 using ::fexcept_t;
67 
68 using ::feclearexcept;
69 using ::fegetexceptflag;
70 using ::feraiseexcept;
71 using ::fesetexceptflag;
72 using ::fetestexcept;
73 using ::fegetround;
74 using ::fesetround;
75 using ::fegetenv;
76 using ::feholdexcept;
77 using ::fesetenv;
78 using ::feupdateenv;
79 
80 _LIBCPP_END_NAMESPACE_STD
81 
82 #endif  // _LIBCPP_CFENV

实际上这里做了两件事情,一是include了C语言的头文件“fenv.h”,二是,将头文件中全局的结构体、对应的函数都使用using语句修饰,保证在对应std 命名空间中都可以正常访问到。这便是C++对C头文件的封装,具体实现还是C语言头文件中的。

fenv.h的结构体定义与宏定义

我们先来看看定义部分:

//http://www.aospxref.com/android-12.0.0_r3/xref/bionic/libc/include/fenv.h
34  #if defined(__aarch64__) || defined(__arm__)
35  #include <bits/fenv_arm.h>
36  #elif defined(__i386__)
37  #include <bits/fenv_x86.h>
38  #elif defined(__x86_64__)
39  #include <bits/fenv_x86_64.h>
40  #endif
...
42  __BEGIN_DECLS
//上面三个平台的函数定义
78  __END_DECLS
...
80  #if defined(__arm__)
81  #include <android/legacy_fenv_inlines_arm.h>
82  #endif

这里通过不同的平台宏进行区分,最后选择了不同的平台头文件,我们以__x86_64__进行分析,其它平台类似(注意,__arm__平台有些区别,其它三个平台的头文件只定义了结构体或者变量,所以需要在该文件中定义函数,但是__arm__平台在其头文件中同时定义了结构体变量和函数,就不需重复定义了)

FE_xxx 异常宏

定义了float运算的相关类别,按位进行标识。

  • FE_INVALID:非法参数异常
  • FE_DENORMAL:非法操作异常
  • FE_DIVBYZERO:除0异常
  • FE_OVERFLOW:上越界溢出异常
  • FE_UNDERFLOW:下越界溢出异常
  • FE_INEXACT:结果不精确异常
  • FE_ALL_EXCEPT:上面所有的异常,按位表示为0x3f
33  /*
34   * Each symbol representing a floating point exception expands to an integer
35   * constant expression with values, such that bitwise-inclusive ORs of _all
36   * combinations_ of the constants result in distinct values.
37   *
38   * We use such values that allow direct bitwise operations on FPU/SSE registers.
39   */
40  #define FE_INVALID    0x01
41  #define FE_DENORMAL   0x02
42  #define FE_DIVBYZERO  0x04
43  #define FE_OVERFLOW   0x08
44  #define FE_UNDERFLOW  0x10
45  #define FE_INEXACT    0x20
46  
47  /*
48   * The following symbol is simply the bitwise-inclusive OR of all floating-point
49   * exception constants defined above.
50   */
51  #define FE_ALL_EXCEPT   (FE_INVALID | FE_DENORMAL | FE_DIVBYZERO | \
52                           FE_OVERFLOW | FE_UNDERFLOW | FE_INEXACT)

FE_xxx 浮点数舍入方向宏

定义了浮点数进行舍入时的方向,按位进行标识(因为浮点数有精度限制,现有的编码方式并不能表示所有的数,需要考虑进行舍入,即使用一个可以的编码来近似表示该浮点数)。

  • FE_TONEAREST:将x舍入为最接近x的值,有一半的case舍入为0
  • FE_DOWNWARD:将x舍入为不大于x的最大值
  • FE_UPWARD:将x舍入为不小于x的最小值
  • FE_TOWARDZERO:将x舍入为绝对值不大于x的最接近x的值
54  /*
55   * Each symbol representing the rounding direction, expands to an integer
56   * constant expression whose value is distinct non-negative value.
57   *
58   * We use such values that allow direct bitwise operations on FPU/SSE registers.
59   */
60  #define FE_TONEAREST  0x000
61  #define FE_DOWNWARD   0x400
62  #define FE_UPWARD     0x800
63  #define FE_TOWARDZERO 0xc00

fenv_t类型定义

该结构体实际上就是整个浮点数运算环境的定义,包括控制字、状态字、标志字等信息。

65  /*
66   * fenv_t represents the entire floating-point environment.
67   */
68  typedef struct {
69    struct {
70      __uint32_t __control;   /* Control word register */
71      __uint32_t __status;    /* Status word register */
72      __uint32_t __tag;       /* Tag word register */
73      __uint32_t __others[4]; /* EIP, Pointer Selector, etc */
74    } __x87;
75    __uint32_t __mxcsr;       /* Control, status register */
76  } fenv_t;

fexcept_t类型定义

异常状态类型信息,具体到函数使用时说明。

78  /*
79   * fexcept_t represents the floating-point status flags collectively, including
80   * any status the implementation associates with the flags.
81   *
82   * A floating-point status flag is a system variable whose value is set (but
83   * never cleared) when a floating-point exception is raised, which occurs as a
84   * side effect of exceptional floating-point arithmetic to provide auxiliary
85   * information.
86   *
87   * A floating-point control mode is a system variable whose value may be set by
88   * the user to affect the subsequent behavior of floating-point arithmetic.
89   */
90  typedef __uint32_t fexcept_t;

fenv.h的变量定义

定义了一个全局只读变量__fe_dfl_env,即我们上面说的fenv_t结构体,保存浮点数运算的相关环境设置信息;同时定义了一个获取该变量地址的宏FE_DFL_ENV,使用取地址符,返回变量地址。

67  /*
68   * The following constant represents the default floating-point environment
69   * (that is, the one installed at program startup) and has type pointer to
70   * const-qualified fenv_t.
71   *
72   * It can be used as an argument to the functions that manage the floating-point
73   * environment, namely fesetenv() and feupdateenv().
74   */
75  extern const fenv_t __fe_dfl_env;
76  #define FE_DFL_ENV (&__fe_dfl_env)

函数定义及源码解析后文分享