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

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

引言

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

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

http://www.aospxref.com/android-12.0.0_r3/xref/

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

C++的封装逻辑

参考对应文件http://www.aospxref.com/android-12.0.0_r3/xref/external/libcxx/include/cfenv中的代码片段如下:

56#include<__config>57#include<fenv.h>5859#if!defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
60#pragmaGCCsystem_header61#endif6263_LIBCPP_BEGIN_NAMESPACE_STD6465using ::fenv_t;
66using ::fexcept_t;
6768using ::feclearexcept;
69using ::fegetexceptflag;
70using ::feraiseexcept;
71using ::fesetexceptflag;
72using ::fetestexcept;
73using ::fegetround;
74using ::fesetround;
75using ::fegetenv;
76using ::feholdexcept;
77using ::fesetenv;
78using ::feupdateenv;
7980_LIBCPP_END_NAMESPACE_STD8182#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.h34  #ifdefined(__aarch64__) ||defined(__arm__)
35  #include<bits/fenv_arm.h>36  #elifdefined(__i386__)
37  #include<bits/fenv_x86.h>38  #elifdefined(__x86_64__)
39  #include<bits/fenv_x86_64.h>40  #endif...
42__BEGIN_DECLS//上面三个平台的函数定义78__END_DECLS...
80#ifdefined(__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 integer35   * constant expression with values, such that bitwise-inclusive ORs of _all36   * 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  #defineFE_INVALID0x0141  #defineFE_DENORMAL0x0242  #defineFE_DIVBYZERO0x0443  #defineFE_OVERFLOW0x0844  #defineFE_UNDERFLOW0x1045  #defineFE_INEXACT0x2046  
47  /*48   * The following symbol is simply the bitwise-inclusive OR of all floating-point49   * exception constants defined above.50   */51  #defineFE_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 integer56   * 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  #defineFE_TONEAREST0x00061  #defineFE_DOWNWARD0x40062  #defineFE_UPWARD0x80063  #defineFE_TOWARDZERO0xc00


fenv_t 类型定义

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

65  /*66   * fenv_t represents the entire floating-point environment.67   */68  typedefstruct {
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, including80   * any status the implementation associates with the flags.81   *82   * A floating-point status flag is a system variable whose value is set (but83   * never cleared) when a floating-point exception is raised, which occurs as a84   * side effect of exceptional floating-point arithmetic to provide auxiliary85   * information.86   *87   * A floating-point control mode is a system variable whose value may be set by88   * the user to affect the subsequent behavior of floating-point arithmetic.89   */90  typedef__uint32_tfexcept_t;


fenv.h 的变量定义

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

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


fenv.h 的函数定义

可以明显地看出,函数定义分为四类:

  • 管理异常 flag 位的
  • 管理浮点数舍入方向的
  • 管理整体环境变量的
  • 管理异常整体控制的
44  // fenv was always available on x86.45  #if__ANDROID_API__>=21||defined(__i386__)
46  intfeclearexcept(int__exceptions) __INTRODUCED_IN_ARM(21) __INTRODUCED_IN_X86(9);
47  intfegetexceptflag(fexcept_t*__flag_ptr, int__exceptions) __INTRODUCED_IN_ARM(21) __INTRODUCED_IN_X86(9);
48  intferaiseexcept(int__exceptions) __INTRODUCED_IN_ARM(21) __INTRODUCED_IN_X86(9);
49  intfesetexceptflag(constfexcept_t*__flag_ptr, int__exceptions) __INTRODUCED_IN_ARM(21) __INTRODUCED_IN_X86(9);
50  intfetestexcept(int__exceptions) __INTRODUCED_IN_ARM(21) __INTRODUCED_IN_X86(9);
51  
52  intfegetround(void) __INTRODUCED_IN_ARM(21) __INTRODUCED_IN_X86(9);
53  intfesetround(int__rounding_mode) __INTRODUCED_IN_ARM(21) __INTRODUCED_IN_X86(9);
54  
55  intfegetenv(fenv_t*__env) __INTRODUCED_IN_ARM(21) __INTRODUCED_IN_X86(9);
56  intfeholdexcept(fenv_t*__env) __INTRODUCED_IN_ARM(21) __INTRODUCED_IN_X86(9);
57  intfesetenv(constfenv_t*__env) __INTRODUCED_IN_ARM(21) __INTRODUCED_IN_X86(9);
58  intfeupdateenv(constfenv_t*__env) __INTRODUCED_IN_ARM(21) __INTRODUCED_IN_X86(9);
59  
60  intfeenableexcept(int__exceptions) __INTRODUCED_IN_ARM(21) __INTRODUCED_IN_X86(9);
61  intfedisableexcept(int__exceptions) __INTRODUCED_IN_ARM(21) __INTRODUCED_IN_X86(9);
62  intfegetexcept(void) __INTRODUCED_IN_ARM(21) __INTRODUCED_IN_X86(9);
63  #else64  /* Defined as inlines for pre-21 ARM. */65  #endif


查看其对应的实现.c 文件

只发现了 amd64,arm64,i387 的实现,具体该选择哪一个呢?我们来看看具体的编译情况。

网络异常,图片无法展示
|

http://www.aospxref.com/android-12.0.0_r3/xref/bionic/libm/Android.bp

22cc_library {
23name: "libm",
24defaults: ["linux_bionic_supported"],
25ramdisk_available: true,
26vendor_ramdisk_available: true,
27recovery_available: true,
28static_ndk_lib: true,
2930whole_static_libs: ["libarm-optimized-routines-math"],
31...
285// arch-specific settings286arch: {
287arm: {
288srcs: [
289"arm/fenv.c",
290             ],
...
314arm64: {
315srcs: [
316"arm64/fenv.c",
317"arm64/lrint.S",
318"arm64/sqrt.S",
319             ],
347x86: {
348srcs: [
349"i387/fenv.c",
...
416x86_64: {
417srcs: [
418"amd64/fenv.c",


可以知道,x86_64 使用的是 amd64/fenv.c 的实现,接下我我们就分析该文件中的具体实现。

http://www.aospxref.com/android-12.0.0_r3/xref/bionic/libm/amd64/fenv.c

管理异常 flag 位

feclearexcept---清空异常检测

作用是清空输入指定的异常,不进行监控。

77  /*78   * The feclearexcept() function clears the supported floating-point exceptions79   * represented by `excepts'.80   */81  int82  feclearexcept(intexcepts)
83  {
84    fenv_tfenv;
85    unsignedintmxcsr;
86  
87    excepts&=FE_ALL_EXCEPT;
88  
89    /* Store the current x87 floating-point environment */90    __asm____volatile__ ("fnstenv %0" : "=m" (fenv));
91  
92    /* Clear the requested floating-point exceptions */93    fenv.__x87.__status&=~excepts;
94  
95    /* Load the x87 floating-point environent */96    __asm____volatile__ ("fldenv %0" : : "m" (fenv));
97  
98    /* Same for SSE environment */99    __asm____volatile__ ("stmxcsr %0" : "=m" (mxcsr));
100    mxcsr&=~excepts;
101    __asm____volatile__ ("ldmxcsr %0" : : "m" (mxcsr));
102  
103    return (0);
104  }


通过代码,可以看到,首先将输入值与 FE_ALL_EXCEPT 做按位与,截断操作数据,保证后续操作是针对有效的异常位;通过__asm__汇编嵌入语句,fnstenv 获取当前的环境变量,然后使用取反再按位与的操作,将想要清除的异常位置为 0,保留其它位,变量为 fenv.__x87.__status;然后 fldenv 加载修改后的环境变量;相同的操作对 SSE(Streaming SIMD Extensions) 环境变量进行处理,主要是对 MXCSR 寄存器进行操作,该寄存器也是管理浮点数运算相关的。

feraiseexcept---更新异常检测

使用输入值更新异常检测环境信息,主要的逻辑还是在 fesetexceptflag 函数中

131  /*132   * The feraiseexcept() function raises the supported floating-point exceptions133   * represented by the argument `excepts'.134   *135   * The standard explicitly allows us to execute an instruction that has the136   * exception as a side effect, but we choose to manipulate the status register137   * directly.138   *139   * The validation of input is being deferred to fesetexceptflag().140   */141  int142  feraiseexcept(intexcepts)
143  {
144    excepts&=FE_ALL_EXCEPT;
145  
146    fesetexceptflag((fexcept_t*)&excepts, excepts);
147    __asm____volatile__ ("fwait");
148  
149    return (0);
150  }


fesetexceptflag---设置异常检测 tag

相关的汇编操作与 feclearexcept 类似,这次 set 操作的两个输入值其实是同一个值,一个只读,一个可改写,对位运算进行两次校验,保证修改正确。

152/*153   * This function sets the floating-point status flags indicated by the argument154   * `excepts' to the states stored in the object pointed to by `flagp'. It does155   * NOT raise any floating-point exceptions, but only sets the state of the flags.156   */157int158fesetexceptflag(constfexcept_t*flagp, intexcepts)
159  {
160fenv_tfenv;
161unsignedintmxcsr;
162163excepts&=FE_ALL_EXCEPT;
164165/* Store the current x87 floating-point environment */166__asm____volatile__ ("fnstenv %0" : "=m" (fenv));
167168/* Set the requested status flags */169fenv.__x87.__status&=~excepts;
170fenv.__x87.__status|=*flagp&excepts;
171172/* Load the x87 floating-point environent */173__asm____volatile__ ("fldenv %0" : : "m" (fenv));
174175/* Same for SSE environment */176__asm____volatile__ ("stmxcsr %0" : "=m" (mxcsr));
177mxcsr&=~excepts;
178mxcsr|=*flagp&excepts;
179__asm____volatile__ ("ldmxcsr %0" : : "m" (mxcsr));
180181return (0);
182  }


fegetexceptflag---读取异常检测 tag

获取对应的异常信息,注意,这里从两个地方获取之后做了与操作,然后返回值通过 flagp 返回

106  /*107   * The fegetexceptflag() function stores an implementation-defined108   * representation of the states of the floating-point status flags indicated by109   * the argument excepts in the object pointed to by the argument flagp.110   */111  int112  fegetexceptflag(fexcept_t*flagp, intexcepts)
113  {
114    unsignedshortstatus;
115    unsignedintmxcsr;
116  
117    excepts&=FE_ALL_EXCEPT;
118  
119    /* Store the current x87 status register */120    __asm____volatile__ ("fnstsw %0" : "=am" (status));
121  
122    /* Store the MXCSR register */123    __asm____volatile__ ("stmxcsr %0" : "=m" (mxcsr));
124  
125    /* Store the results in flagp */126    *flagp= (status|mxcsr) &excepts;
127  
128    return (0);
129  }


fetestexcept---检测异常标志位

测试对应的标志位,原理与 get 操作基本一致,但是返回值是对应的 flag 信息

184  /*185   * The fetestexcept() function determines which of a specified subset of the186   * floating-point exception flags are currently set. The `excepts' argument187   * specifies the floating-point status flags to be queried.188   */189  int190  fetestexcept(intexcepts)
191  {
192    unsignedshortstatus;
193    unsignedintmxcsr;
194  
195    excepts&=FE_ALL_EXCEPT;
196  
197    /* Store the current x87 status register */198    __asm____volatile__ ("fnstsw %0" : "=am" (status));
199  
200    /* Store the MXCSR register state */201    __asm____volatile__ ("stmxcsr %0" : "=m" (mxcsr));
202  
203    return ((status|mxcsr) &excepts);
204  }


管理浮点数舍入方向

fegetround---读取舍入信息

通过 fnstcw 指令获取返回值 control,并与 X87_ROUND_MASK 做按位与之后返回,也是为了保证是四个有效值中的一个。

46#defineX87_ROUND_MASK  (FE_TONEAREST|FE_DOWNWARD|FE_UPWARD|FE_TOWARDZERO)
...
206  /*207   * The fegetround() function gets the current rounding direction.208   */209  int210  fegetround(void)
211  {
212    unsignedshortcontrol;
213  
214    /*215     * We assume that the x87 and the SSE unit agree on the216     * rounding mode.  Reading the control word on the x87 turns217     * out to be about 5 times faster than reading it on the SSE218     * unit on an Opteron 244.219     */220    __asm____volatile__ ("fnstcw %0" : "=m" (control));
221  
222    return (control&X87_ROUND_MASK);
223  }


fesetround---设置舍入信息

fesetround 的操作略微复杂:先验证输入 round 值得有效性,通过位运算处理,如果还不为 0,说明输入参数是非法的,不为四个有效值的组合,直接返回-1 表示 error;通过 fnstcw/fldcw 读写寄存器,将有效信息写入寄存器。注意这里 MXCSR 寄存器是 32 位位宽,它的具体信息如下:

  • SIMD 浮点异常的标志位与掩码位
  • SIMD 浮点操作的舍入控制域
  • 下溢清零标志位(flush-to-zero),控制当 SIMD 浮点操作出现下溢时的结果
  • 非规格化数据作零标志位(denormals-are-zero),控制当 SIMD 浮点操作处理遇到非规格化源操作数时的行为

网络异常,图片无法展示
|

所以进行舍入控制时,原本 FE_TONEAREST  0x000,FE_DOWNWARD   0x400,FE_UPWARD     0x800,FE_TOWARDZERO 0xc00 进行移位,对应上面的 0x000,0x2000,0x4000,0x6000,分别是

  • flush-to-zero 为 0,round control 为 0:默认最近舍入
  • flush-to-zero 为 0,round control 为 2:向下舍入
  • flush-to-zero 为 1,round control 为 0:向上舍入
  • flush-to-zero 为 1,round control 为 2:向下舍入,且绝对值最近的
46#defineX87_ROUND_MASK  (FE_TONEAREST|FE_DOWNWARD|FE_UPWARD|FE_TOWARDZERO)
47#defineSSE_ROUND_SHIFT3225  /*226   * The fesetround() function establishes the rounding direction represented by227   * its argument `round'. If the argument is not equal to the value of a rounding228   * direction macro, the rounding direction is not changed.229   */230  int231  fesetround(intround)
232  {
233    unsignedshortcontrol;
234    unsignedintmxcsr;
235  
236    /* Check whether requested rounding direction is supported */237    if (round&~X87_ROUND_MASK)
238      return (-1);
239  
240    /* Store the current x87 control word register */241    __asm____volatile__ ("fnstcw %0" : "=m" (control));
242  
243    /* Set the rounding direction */244    control&=~X87_ROUND_MASK;
245    control|=round;
246  
247    /* Load the x87 control word register */248    __asm____volatile__ ("fldcw %0" : : "m" (control));
249  
250    /* Same for the SSE environment */251    __asm____volatile__ ("stmxcsr %0" : "=m" (mxcsr));
252    mxcsr&=~(X87_ROUND_MASK<<SSE_ROUND_SHIFT);
253    mxcsr|=round<<SSE_ROUND_SHIFT;
254    __asm____volatile__ ("ldmxcsr %0" : : "m" (mxcsr));
255  
256    return (0);
257  }


管理整体环境变量

fegetenv---获取环境变量

通过对应的汇编命令返回对应的数据结构,如 x87 整体的数据,MXCSR 寄存器值,最后使用 fldcw 再更新__control 的值(控制舍入的寄存器位)

259  /*260   * The fegetenv() function attempts to store the current floating-point261   * environment in the object pointed to by envp.262   */263  int264  fegetenv(fenv_t*envp)
265  {
266    /* Store the current x87 floating-point environment */267    __asm____volatile__ ("fnstenv %0" : "=m" (*envp));
268  
269    /* Store the MXCSR register state */270    __asm____volatile__ ("stmxcsr %0" : "=m" (envp->__mxcsr));
271  
272    /*273     * When an FNSTENV instruction is executed, all pending exceptions are274     * essentially lost (either the x87 FPU status register is cleared or275     * all exceptions are masked).276     *277     * 8.6 X87 FPU EXCEPTION SYNCHRONIZATION -278     * Intel(R) 64 and IA-32 Architectures Softare Developer's Manual - Vol1279     */280    __asm____volatile__ ("fldcw %0" : : "m" (envp->__x87.__control));
281  
282    return (0);
283  }


feholdexcept---保存异常然后 Reset 打开所有异常

该函数会将当前的状态保存到入参中,然后将所有的异常检测都打开,后面可以通过前面保存的状态重新恢复之前的状态,类似一个快照,在代码中的某些部分不能够屏蔽异常,所以需要先保存快照,然后等这段代码过之后再重新屏蔽异常。

285  /*286   * The feholdexcept() function saves the current floating-point environment287   * in the object pointed to by envp, clears the floating-point status flags, and288   * then installs a non-stop (continue on floating-point exceptions) mode, if289   * available, for all floating-point exceptions.290   */291  int292  feholdexcept(fenv_t*envp)
293  {
294    unsignedintmxcsr;
295  
296    /* Store the current x87 floating-point environment */297    __asm____volatile__ ("fnstenv %0" : "=m" (*envp));
298  
299    /* Clear all exception flags in FPU */300    __asm____volatile__ ("fnclex");
301  
302    /* Store the MXCSR register state */303    __asm____volatile__ ("stmxcsr %0" : "=m" (envp->__mxcsr));
304  
305    /* Clear exception flags in MXCSR */306    mxcsr=envp->__mxcsr;
307    mxcsr&=~FE_ALL_EXCEPT;
308  
309    /* Mask all exceptions */310    mxcsr|=FE_ALL_EXCEPT<<SSE_MASK_SHIFT;
311  
312    /* Store the MXCSR register */313    __asm____volatile__ ("ldmxcsr %0" : : "m" (mxcsr));
314  
315    return (0);
316  }


fesetenv--设置环境变量

使用 fldenv、ldmxcsr 命令

318  /*319   * The fesetenv() function attempts to establish the floating-point environment320   * represented by the object pointed to by envp. The argument `envp' points321   * to an object set by a call to fegetenv() or feholdexcept(), or equal a322   * floating-point environment macro. The fesetenv() function does not raise323   * floating-point exceptions, but only installs the state of the floating-point324   * status flags represented through its argument.325   */326  int327  fesetenv(constfenv_t*envp)
328  {
329    /* Load the x87 floating-point environent */330    __asm____volatile__ ("fldenv %0" : : "m" (*envp));
331  
332    /* Store the MXCSR register */333    __asm____volatile__ ("ldmxcsr %0" : : "m" (envp->__mxcsr));
334  
335    return (0);
336  }


feupdateenv---更新环境变量

实际上是调用前面 fesetenv 和 feraiseexcept 的组合完成。

338/*339   * The feupdateenv() function saves the currently raised floating-point340   * exceptions in its automatic storage, installs the floating-point environment341   * represented by the object pointed to by `envp', and then raises the saved342   * floating-point exceptions. The argument `envp' shall point to an object set343   * by a call to feholdexcept() or fegetenv(), or equal a floating-point344   * environment macro.345   */346int347feupdateenv(constfenv_t*envp)
348  {
349unsignedshortstatus;
350unsignedintmxcsr;
351352/* Store the x87 status register */353__asm____volatile__ ("fnstsw %0" : "=am" (status));
354355/* Store the MXCSR register */356__asm____volatile__ ("stmxcsr %0" : "=m" (mxcsr));
357358/* Install new floating-point environment */359fesetenv(envp);
360361/* Raise any previously accumulated exceptions */362feraiseexcept(status|mxcsr);
363364return (0);
365  }


管理异常整体控制

feenableexcept---使能新的异常 mask,返回旧的异常 mask

首先截断输入 mask,保证其有效性,在设置处理上也比较好理解,取反之后再与运算,就是只 enablemask 指定的异常检测,MXCSR 寄存器的操作需要进行移位,左移 7 位正好对应到 Invalid Operation Mask 及之后的位。

网络异常,图片无法展示
|

这里比较特殊的是还将改变前的 control 和 mxcsr 的信息做了组合,然后取反,即返回之前 enable 的异常 mask.

40#defineSSE_MASK_SHIFT7367  /*368   * The following functions are extentions to the standard369   */370  int371  feenableexcept(intmask)
372  {
373    unsignedintmxcsr, omask;
374    unsignedshortcontrol;
375  
376    mask&=FE_ALL_EXCEPT;
377  
378    __asm____volatile__ ("fnstcw %0" : "=m" (control));
379    __asm____volatile__ ("stmxcsr %0" : "=m" (mxcsr));
380  
381    omask=~(control| (mxcsr>>SSE_MASK_SHIFT)) &FE_ALL_EXCEPT;
382    control&=~mask;
383    __asm____volatile__ ("fldcw %0" : : "m" (control));
384  
385    mxcsr&=~(mask<<SSE_MASK_SHIFT);
386    __asm____volatile__ ("ldmxcsr %0" : : "m" (mxcsr));
387  
388    return (omask);
389  }


fedisableexcept---不使能新的异常 mask,返回旧的异常 mask

操作与 feenableexcept 类似,只是在设置之前直接做或运算

391  int392  fedisableexcept(intmask)
393  {
394    unsignedintmxcsr, omask;
395    unsignedshortcontrol;
396  
397    mask&=FE_ALL_EXCEPT;
398  
399    __asm____volatile__ ("fnstcw %0" : "=m" (control));
400    __asm____volatile__ ("stmxcsr %0" : "=m" (mxcsr));
401  
402    omask=~(control| (mxcsr>>SSE_MASK_SHIFT)) &FE_ALL_EXCEPT;
403    control|=mask;
404    __asm____volatile__ ("fldcw %0" : : "m" (control));
405  
406    mxcsr|=mask<<SSE_MASK_SHIFT;
407    __asm____volatile__ ("ldmxcsr %0" : : "m" (mxcsr));
408  
409    return (omask);
410  }


fegetexcept---返回异常 mask

返回取反之后的 mask 与 FE_ALL_EXCEPT 求与,即当前有哪些异常使能了。

注意:这里假设通过 fnstcw 获取的信息与 MXCSR 寄存器的值一致,所以就没有再获取 MXCSR 寄存器的信息进行判断。

412  int413  fegetexcept(void)
414  {
415    unsignedshortcontrol;
416  
417    /*418     * We assume that the masks for the x87 and the SSE unit are419     * the same.420     */421    __asm____volatile__ ("fnstcw %0" : "=m" (control));
422  
423    return (~control&FE_ALL_EXCEPT);
424  }


总结

通过对该文件的源码阅读,我们了解了浮点数异常处理机制和舍入机制的设置,实际上在 x86_64 架构下,有两个信息会有影响,即 fenv 信息、MXCSR 寄存器,它们都有对应的汇编指令进行读取和写入,我们上层的函数操作也是基于对这两种信息的读取、修改、写入上,实际还是汇编代码的操作。同时,在这个过程中也有相当多的位运算方式,通过位运算方式能够有效与寄存器交互,加快我们的运算效率。

相关文章
|
11天前
|
存储 C++ 容器
C++STL(标准模板库)处理学习应用案例
【4月更文挑战第8天】使用C++ STL,通过`std:vector`存储整数数组 `{5, 3, 1, 4, 2}`,然后利用`std::sort`进行排序,输出排序后序列:`std:vector<int> numbers; numbers = {5, 3, 1, 4, 2}; std:sort(numbers.begin(), numbers.end()); for (int number : numbers) { std::cout << number << " "; }`
17 2
|
22天前
|
存储 安全 编译器
C++学习过程中的一些值得注意的小点(1)
C++学习过程中的一些值得注意的小点(1)
|
23天前
|
存储 算法 数据库
【C++ 软件设计思路】学习C++中如何生成唯一标识符:从UUID到自定义规则
【C++ 软件设计思路】学习C++中如何生成唯一标识符:从UUID到自定义规则
103 0
|
30天前
|
IDE 编译器 开发工具
C/C++ IDE环境 (Qt Creator visual studio等) Cmake工程不显示头文件的解决方案
C/C++ IDE环境 (Qt Creator visual studio等) Cmake工程不显示头文件的解决方案
21 0
|
29天前
|
编译器 测试技术 C++
【Python 基础教程 01 全面介绍】 Python编程基础全攻略:一文掌握Python语法精髓,从C/C++ 角度学习Python的差异
【Python 基础教程 01 全面介绍】 Python编程基础全攻略:一文掌握Python语法精髓,从C/C++ 角度学习Python的差异
159 0
|
11天前
|
程序员 C++
C++语言模板学习应用案例
C++模板实现通用代码,以适应多种数据类型。示例展示了一个计算两数之和的模板函数`add&lt;T&gt;`,可处理整数和浮点数。在`main`函数中,展示了对`add`模板的调用,分别计算整数和浮点数的和,输出结果。
10 2
|
23天前
|
算法 安全 编译器
【C++ 17 新特性 折叠表达式 fold expressions】理解学习 C++ 17 折叠表达式 的用法
【C++ 17 新特性 折叠表达式 fold expressions】理解学习 C++ 17 折叠表达式 的用法
23 1
|
25天前
|
设计模式 算法 测试技术
【C/C++ 编程 哑对象】了解和学习哑对象在C/C++ 编程中的使用
【C/C++ 编程 哑对象】了解和学习哑对象在C/C++ 编程中的使用
43 2
|
1月前
|
编译器 C语言 C++
C/C++编译优化技巧:预编译头文件(PCH)使用方法
C/C++编译优化技巧:预编译头文件(PCH)使用方法
16 1
|
1月前
|
JavaScript 前端开发 编译器
Cmake 中 compiler_depend.ts 文件:解析和使用 C/C++ 预编译头文件
Cmake 中 compiler_depend.ts 文件:解析和使用 C/C++ 预编译头文件
22 1