引言
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 寄存器,它们都有对应的汇编指令进行读取和写入,我们上层的函数操作也是基于对这两种信息的读取、修改、写入上,实际还是汇编代码的操作。同时,在这个过程中也有相当多的位运算方式,通过位运算方式能够有效与寄存器交互,加快我们的运算效率。