4.2.5. C99 兼容函数

除了前面介绍的函数外,ARM 编译器还支持在 C99 标准中定义的一组函数。

这些 C99 兼容函数是可用于安装自定义异常捕获处理程序(具有生成返回值的功能)的唯一接口。 本节中的所有函数、类型和宏都是在 fenv.h 中定义的。

C99 定义了以下两种数据类型:fenv_tfexcept_t。 C99 标准未提供有关这些类型的任何信息,因此,对于可移植代码,必须将这些类型视为不透明的类型。 ARM 编译器将它们定义为结构类型。 有关详细信息,请参阅ARM 编译器的 C99 接口扩展

fenv_t 类型被定义为存储有关当前浮点环境的所有信息:

fexcept_t 类型被定义为存储与一组给定异常相关的所有信息。

C99 舍入模式和异常宏

C99 还为每种舍入模式和每个异常定义了一个宏。 这些宏如下所示:


FE_DIVBYZERO

FE_INEXACT

FE_INVALID

FE_OVERFLOW

FE_UNDERFLOW

FE_ALL_EXCEPT

FE_DOWNWARD

FE_TONEAREST

FE_TOWARDZERO

FE_UPWARD

异常宏是位域。 FE_ALL_EXCEPT 宏是所有这些字段的按位“或”运算。

处理异常标记

C99 提供了以下三个用于清除、测试和产生异常的函数:

void feclearexcept(int excepts);

int fetestexcept(int excepts);

void feraiseexcept(int excepts);

feclearexcept 函数清除给定异常的粘性标记。 fetestexcept 函数返回给定异常的粘性标记的按位“或”运算结果(因此,如果设置了“溢出”标记而未设置“下溢”标记,fetestexcept(FE_OVERFLOW|FE_UNDERFLOW) 调用将返回 FE_OVERFLOW)。

feraiseexcept 函数按未指定的顺序产生给定的异常。 如果为以这种方式产生的异常启用了异常捕获,则会调用该函数。

C99 还提供了一些函数以保存和恢复与给定异常有关的所有内容。 这包括粘性标记、是否捕获异常以及捕获处理程序地址(如果有)。 这些函数是:

void fegetexceptflag(fexcept_t *flagp, int excepts);

void fesetexceptflag(const fexcept_t *flagp, int excepts);

fegetexceptflag 函数将与给定异常有关的所有信息复制到提供的 fexcept_t 变量中。 fesetexceptflag 函数将与给定异常有关的所有信息从 fexcept_t 变量复制到当前浮点环境中。

Note

可以使用 fesetexceptflag 将捕获的异常的粘性标记设置为 1,而无需调用捕获处理程序,但 feraiseexcept 会为所有捕获的异常调用捕获处理程序。

处理舍入模式

C99 提供了以下两个用于控制舍入模式的函数:

int fegetround(void);

int fesetround(int round);

fegetround 函数返回当前舍入模式,该模式的值等于C99 舍入模式和异常宏中列出的一个宏的值。 fesetround 函数将当前舍入模式设置为提供的值。如果成功,fesetround 将返回零;如果其自变量不是有效舍入模式,则返回非零值。

保存整个环境

C99 提供了一些用于保存和恢复整个浮点环境的函数:

void fegetenv(fenv_t *envp);

void fesetenv(const fenv_t *envp);

fegetenv 函数将浮点环境的当前状态存储到提供的 fenv_t 变量中。 fesetenv 函数从提供的变量中恢复环境。

fesetexceptflag 一样,fesetenv 在为捕获的异常设置粘性标记时不调用捕获处理程序。

临时禁用异常

C99 提供了两个具有以下功能的函数:规避了在执行可能导致出现异常的代码时进行异常捕获的风险。 这是非常有用的,例如,在捕获的异常使用 ARM 缺省行为时。 缺省行为是导致生成 SIGFPE 并终止应用程序。

int feholdexcept(fenv_t *envp);

void feupdateenv(const fenv_t *envp);

feholdexcept 函数将当前浮点环境保存在提供的 fenv_t 变量中、设置所有不捕获的异常并清除所有异常粘性标记。 可随后执行可能导致出现不需要的异常的代码,并确保清除这些异常的粘性标记。 然后,可以调用 feupdateenv。 这将恢复所有异常捕获,并在必要时对其进行调用。

例如,假定有一个 frob() 函数可能导致出现“下溢”或“无效运算”异常(假定捕获这两个异常)。 您对“下溢”不感兴趣,但想知道是否进行了无效运算, 因此,可以执行以下命令:


fenv_t env;

feholdexcept(&env);

frob();

feclearexcept(FE_UNDERFLOW);

feupdateenv(&env);

随后,如果 frob() 函数产生“下溢”,feclearexcept 则会再次将其清除,因此在调用 feupdateenv 时不会发生捕获。 但是,如果 frob() 产生“无效运算”,在调用 feupdateenv 时将设置粘性标记,因而会调用捕获处理程序。

此机制是由 C99 提供的,因为 C99 没有指定为各个异常更改异常捕获的方式。 更好的方法是使用 __ieee_status 禁用“下溢”捕获,而将“无效运算”捕获保持启用。 这样做的好处是,可以为“无效运算”捕获处理程序提供与无效运算有关的所有信息(即,对哪些数据执行了哪些运算),并且可以生成该运算的结果。 使用 C99 方法时,将在出现异常后调用“无效运算”捕获处理程序,不会接收与异常原因有关的任何信息,并且由于调用时间太晚而无法提供替代结果。

Copyright © 2007 ARM Limited. All rights reserved. ARM DUI 0349AC
Non-Confidential