為了編寫高精度浮點(diǎn)數(shù)的運(yùn)算,編程人員需要控制浮點(diǎn)數(shù)環(huán)境的各個(gè)方面:結(jié)果如何舍入,浮點(diǎn)數(shù)表達(dá)式如何簡化與變換,如何處理浮點(diǎn)數(shù)異常(如下溢之類的浮點(diǎn)數(shù)異常是忽略還是產(chǎn)生錯(cuò)誤),等等。C99引入了fenv.h來控制浮點(diǎn)數(shù)環(huán)境。
1、fenv.h: 定義了浮點(diǎn)數(shù)環(huán)境控制函數(shù)、異常控制函數(shù)、舍入方式控制函數(shù)、浮點(diǎn)數(shù)異常碼和舍入方式等。注意浮點(diǎn)數(shù)環(huán)境的實(shí)現(xiàn)是依賴于體系結(jié)構(gòu)的,因?yàn)椴煌捏w系結(jié)構(gòu)有不同的浮點(diǎn)數(shù)指令集。依賴于體系結(jié)構(gòu)的定義放在bits/fenv.h中,這里是x86體系結(jié)構(gòu)的版本(Linux下)。
標(biāo)準(zhǔn)頭文件fenv.h內(nèi)容如下:
-
- #ifndef _FENV_H
- #define _FENV_H 1
- #include <features.h>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- #include <bits/fenv.h>
- __BEGIN_DECLS
-
-
- extern int feclearexcept (int __excepts) __THROW;
-
- extern int fegetexceptflag (fexcept_t *__flagp, int __excepts) __THROW;
-
- extern int feraiseexcept (int __excepts) __THROW;
-
- extern int fesetexceptflag (__const fexcept_t *__flagp, int __excepts) __THROW;
-
- extern int fetestexcept (int __excepts) __THROW;
-
-
- extern int fegetround (void) __THROW;
-
- extern int fesetround (int __rounding_direction) __THROW;
-
-
- extern int fegetenv (fenv_t *__envp) __THROW;
-
-
- extern int feholdexcept (fenv_t *__envp) __THROW;
-
- extern int fesetenv (__const fenv_t *__envp) __THROW;
-
-
- extern int feupdateenv (__const fenv_t *__envp) __THROW;
-
-
- #ifdef __OPTIMIZE__
- # include <bits/fenvinline.h>
- #endif
- #ifdef __USE_GNU
-
-
- extern int feenableexcept (int __excepts) __THROW;
-
-
- extern int fedisableexcept (int __excepts) __THROW;
-
- extern int fegetexcept (void) __THROW;
- #endif
- __END_DECLS
- #endif /* fenv.h */
bits/fenv.h內(nèi)容如下:
-
- #ifndef _FENV_H
- # error "Never use <bits/fenv.h> directly; include <fenv.h> instead."
- #endif
- #include <bits/wordsize.h>
-
- enum
- {
- FE_INVALID = 0x01,
- #define FE_INVALID FE_INVALID
- __FE_DENORM = 0x02,
- FE_DIVBYZERO = 0x04,
- #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)
-
- 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;
-
-
- 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
標(biāo)準(zhǔn)C中指定的接口有:
(1)浮點(diǎn)數(shù)環(huán)境類型:fenv_t,是一個(gè)結(jié)構(gòu)體,里面的成員依賴于體系結(jié)構(gòu)。
(2)浮點(diǎn)數(shù)環(huán)境的缺省值:FE_DFL_ENV,是一個(gè)fenv_t *指針類型的值(為-1),用于需要fenv_t參數(shù)的函數(shù)中。
(3)浮點(diǎn)數(shù)異常標(biāo)志類型:fexcept_t,為unsigned short類型。異常標(biāo)志中保存了浮點(diǎn)數(shù)的狀態(tài)。
(4)浮點(diǎn)數(shù)異常:FE_INEXACT, FE_DIVBYZERO, FE_UNDERFLOW, FE_OVERFLOW, FE_INVALID, FE_ALL_EXCEPT。
(5)浮點(diǎn)數(shù)舍入方式:FE_TONEAREST, FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO。
(6)浮點(diǎn)數(shù)環(huán)境控制函數(shù):fegetenv, fesetenv, feholdexcpet, feupdateenv。
(7)浮點(diǎn)數(shù)異常處理函數(shù):fegetexceptflag, fesetexceptflag, fetestexcept, feraiseexcept, feclearexcept。
(8)浮點(diǎn)數(shù)舍入控制函數(shù):fegetround, fesetround。
2、浮點(diǎn)數(shù)環(huán)境、異常、舍入控制函數(shù)的實(shí)現(xiàn): 這些函數(shù)的實(shí)現(xiàn)都是依賴于體系結(jié)構(gòu)的,x86體系結(jié)構(gòu)的部分在glibc源碼的sysdeps/i386/fpu目錄下。每個(gè)函數(shù)對應(yīng)一個(gè)C語言源文件,由于要用到浮點(diǎn)數(shù)操作指令,因此都嵌入了匯編代碼。這些函數(shù)的功能都非常簡單,主要是通過fenv_t的實(shí)例或fexcept_t的實(shí)例來獲取或設(shè)置相應(yīng)的值,因此代碼都不長,要理解這些代碼,主要是要對x86體系結(jié)構(gòu)有全面的了解。由于代碼依賴于體系結(jié)構(gòu),也沒有特別值得研究的獨(dú)立于體系結(jié)構(gòu)的算法,因此這里并不打算解剖它們的源代碼。