如何在C语言中检测浮点下溢
How to detect floating point underflow in C
我需要帮助和示例代码,了解如何以标准方式检测浮点下溢,而不使用第三方库以及有符号和无符号的异常。我在谷歌上搜索了一下,发现许多作者都在谈论"逐渐的底流算不算是底流"?谁能给我解释一下什么是渐下?我需要检查两种情况
感谢您的时间和帮助
看一下"逐渐下沉"的搜索结果并没有给出一个清晰直接的答案,所以:
IEEE-754二进制浮点数在其大部分范围内具有规则模式:有一个符号,一个指数和一个具有一定位数的有效位数(32位float
为24,64位double
为53)。然而,图案必须在末端被打断。在高端,对于最大指数来说太大的结果将被更改为无穷大。在低端,我们有一个选择。
一种选择是,如果结果低于最低指数,则结果四舍五入为零。然而,IEEE-754使用了一种不同的方案,称为逐渐下流。最低指数的保留格式与常规指数的保留格式不同。
对于正常指数,24位有效位数为"1"。,后面是在有效位域中编码的23位。当数字为次正规时,指数与最低正规指数的值相同,但24位有效位数为"0"。,后面跟着23位。这是渐进的下溢,因为随着数字越来越小,在达到零之前,它们的精度越来越低(有效位数的前导位越来越多为零)。
逐渐下溢具有一些很好的数学性质,特别是a-b == 0
当且仅当a == b
。由于a-b
太小,无法用浮点格式表示,所以即使a
和b
不同,也可能出现a-b == 0
。随着逐渐溢出,a-b
的所有可能值,对于较小的a
和b
,都是可表示的,因为它们只是指数最低的有效数的差异。
确定是否发生浮点数下溢的另一个问题是,实现允许(根据IEEE-754标准)基于舍入之前或之后的测试报告下溢。在计算结果时,浮点实现必须有效地执行以下步骤:
- 计算精确结果的符号、指数和有效数。
- 将结果四舍五入以适应浮点格式。如果有效数四舍五入,这可能会增加指数。
该标准允许使用以下两种方法报告下溢:
- 计算精确结果的符号、指数和有效数。
- 指数是否小于正常范围?
- 将结果四舍五入以适应浮点格式。如果有效数四舍五入,这可能会增加指数。
或:
- 计算精确结果的符号、指数和有效数。
- 将结果四舍五入以适应浮点格式。如果有效数四舍五入,这可能会增加指数。
- 指数是否小于正常范围?
因此,对于相同的计算,两个不同的浮点数实现可能返回不同的关于下溢的报告。
有一些关于处理下溢的附加规则。上述操作导致发出下流异常信号。但是,如果此异常的陷阱未启用并且结果是精确的[舍入没有改变任何东西],则忽略"underflow",并且不会引发underflow标志。如果结果不精确,则引发下流并发出不精确异常。)
检查浮点状态字是否有'underflow'位,或者捕获下流浮点异常信号。看到:
http://www.gnu.org/software/libc/manual/html_node/FP-Exceptions.htmltemplate <typename T>
class Real
{
public:
Real(T x) : x_(x) { }
Real& operator/=(T rhs)
{
if (x_)
{
x_ /= rhs;
if (!x_)
do whatever you want for underflow...
}
}
friend Real operator/(Real lhs, Real rhs)
{ return lhs /= rhs; }
// similar for -= etc.
private:
T x_;
}
首先,您需要在构建中启用浮点异常的生成。其次,您必须在代码中捕获它们。
我做了这样的事情(使用Visual Studio)
void translateFPException( unsigned int u, EXCEPTION_POINTERS* pExp )
{
unsigned int fpuStatus = _clearfp(); // clear the exception
switch (u)
{
case STATUS_FLOAT_DENORMAL_OPERAND:
throw fe_denormal_operand(fpuStatus);
case STATUS_FLOAT_DIVIDE_BY_ZERO:
throw fe_divide_by_zero(fpuStatus);
case STATUS_FLOAT_INEXACT_RESULT:
throw fe_inexact_result(fpuStatus);
case STATUS_FLOAT_INVALID_OPERATION:
throw fe_invalid_operation(fpuStatus);
case STATUS_FLOAT_OVERFLOW:
throw fe_overflow(fpuStatus);
case STATUS_FLOAT_UNDERFLOW:
throw fe_underflow(fpuStatus);
case STATUS_FLOAT_STACK_CHECK:
throw fe_stack_check(fpuStatus);
default:
throw float_exception(fpuStatus);
};
}
void initializeFloatingPointExceptionHandling()
{
unsigned int fpControlWord = 0x00;
_clearfp(); // always call _clearfp before enabling/unmasking a FPU exception
// enabling an exception is done by clearing the respective bit in the control word
errno_t success = _controlfp_s( &fpControlWord,
0xffffffff^( _EM_INVALID
| _EM_ZERODIVIDE
| _EM_OVERFLOW
| _EM_DENORMAL
| _EM_UNDERFLOW
| _EM_INEXACT), _MCW_EM );
if (success != 0)
{
stringstream errStream;
errStream << success << " " << __FILE__ << ":" << __LINE__ << std::endl;
throw exception(errStream.str().c_str());
}
_se_translator_function old =_set_se_translator(translateFPException);
}
void enableAllFPUExceptions()
{
unsigned int oldMask = 0x00000000;
_clearfp();
// enabling is done by clearing the respective bit in the mask
_controlfp_s(&oldMask, 0x00, _MCW_EM);
}
void disableAllFPUExceptions()
{
unsigned int oldMask = 0x00000000;
_clearfp();
// disabling is done by setting the respective bit in the mask
_controlfp_s(&oldMask, 0xffffffff, _MCW_EM);
}
然后你必须编写你自己的异常(也只是一个摘录,但你应该了解这个概念)
class float_exception : public exception
{
public:
float_exception(unsigned int fpuStatus);
unsigned int getStatus();
protected:
unsigned int fpuStatus;
};
class fe_denormal_operand : public float_exception
{
public:
fe_denormal_operand(unsigned int fpuStatus);
};
- 自定义 std::fstream,std::filebuf 的溢出和下溢函数未为每个字符调用
- 带有剪裁下溢的夏亚减法
- 使用 size_t 初始化循环变量时的整数下溢
- 无锁队列实现中的虚假下溢C++
- 在减法期间将 C++ 转换为字节 (unit8_t) 不会像我预期的那样强制下溢;输出int16_t;为什么?
- C++长 int 溢出/下溢
- 上溢/下溢是执行时未定义的行为吗
- C++ 在将字符串提取到标量时处理溢出/下溢
- C/FORTRAN将双下溢设置为零
- basic_streambuf炒锅的功能溢出和下溢如何
- 为什么C++下溢/溢出行为被视为未定义
- C++下溢和溢出
- 渐变下溢程序的表示
- 指针下溢或溢出时会发生什么情况
- 溢流/下溢问题
- 如何在C语言中检测浮点下溢
- 如何检测双精度浮点溢出和下溢
- 未签名的下溢机制
- 编译器构造-如何在C++中发生堆栈下溢
- 将std::string转换为integer时,如何处理上溢/下溢