Before I start, just some background information:
我正在使用编译器标准数学库(符合IEEE-754)在Keil uVision3中编译的ARM7微控制器(LPC2294 / 01)上运行裸机应用程序 .
The issue: I 'm having trouble wrapping my head around what exactly constitutes an '溢出'对2个单精度浮点输入的总和 .
最初,我的印象是,如果我试图将任何正值添加到可以用IEEE-754表示法表示的最大值,结果将产生溢出异常 .
例如,假设我有:
a = 0x7f7fffff (ie. 3.4028235..E38);
b = 0x3f800000 (ie. 1.0)
我期望将这两个值相加会导致IEEE-754中定义的溢出 . 令我最初的惊讶,结果只返回'a'的值,没有标记异常 .
所以我想,因为精确度(或者你喜欢的分辨率)随着所表示的值的增加而减小,所以在这种情况下,值“1”可能由于其相对不重要而有效地向下舍入到0 .
So that begged the question: 在这种情况下,'b'的最小值会导致溢出异常吗?它取决于IEEE-754的具体实现吗?
也许就像我不了解如何在这种特殊情况下确定最小“重要”精度一样简单,但是考虑到下面的代码,为什么第二个和会导致溢出而不是第一个?
static union sFloatConversion32
{
unsigned int unsigned32Value;
float floatValue;
} sFloatConversion32;
t_bool test_Float32_Addition(void)
{
float a;
float b;
float c;
sFloatConversion32.unsigned32Value = 0x7f7fffff;
a = sFloatConversion32.floatValue;
sFloatConversion32.unsigned32Value = 0x72ffffff;
b = sFloatConversion32.floatValue;
/* This sum returns (c = a) without overflow */
c = a + b;
sFloatConversion32.unsigned32Value = 0x73000000;
b = sFloatConversion32.floatValue;
/* This sum, however, causes an overflow exception */
c = a + b;
}
是否存在可以应用的通用规则,使得可以提前知道(即,不执行总和),给定两个浮点数,它们的总和将导致IEEE-754定义的溢出?
3 回答
当结果受格式范围影响时,会发生溢出 . 只要正常舍入将结果保持在有限范围内,就不会发生溢出,因为结果与指数无界时的结果相同 - 在考虑范围之前,结果通过正常舍入减少 . 所以由于范围没有例外 .
当舍入结果不适合格式的有限范围时,则无法生成有限结果,因此会发生溢出异常并产生无穷大 .
在IEEE 754中,正常操作实际上是两个步骤:
计算确切的数学结果 .
将精确的数学结果舍入到最接近的可表示值 .
当且仅当上述结果的大小超过最大可表示的有限值时,IEEE 754定义溢出 . 换句话说,溢出不会仅因为你超过最大可表示值而发生,但只有当你远远超过最大可表示值时,算术在浮点运行的常规方法才起作用 .
因此,如果从最大的可表示值开始并向其添加一个小数字,结果将简单地舍入到最大可表示值(当使用舍入到最近时) . IEEE 754认为这是正常的 - 所有算术运算都是圆的,并且如果该舍入使结果保持在边界内,则这是正常且无法实现的 . 即使指数范围是无界的,正常的舍入也会产生相同的结果 . 由于这是不受有限范围影响的正常结果,因此没有发生任何异常情况 .
只有当数学结果如此之大以至于如果我们不受指数限制,舍入将产生下一个更高的数字时,才会发生溢出 . (但是,既然我们已达到指数范围的极限,我们必须返回无穷大 . )
IEEE-754基本32位二进制浮点中可表示的最大值为2128-2104 . 此时,可表示数字之间的步长以2104为单位 . 使用舍入到最接近的规则,将少于半步的任何数字2103加到此处将舍入到2128-2104,并且不会发生溢出 . 如果你添加一个大于2103的数字,那么如果指数可以达到那么高,结果将舍入到2128 . 相反,会产生无穷大并发生溢出异常 . (如果你精确地添加了2103,则使用关系规则 . 这个规则说选择具有偶数低位的候选者 . 这会产生2128,所以它也会溢出 . )
因此,对于圆到最近,溢出发生在步的中点 . 使用其他舍入规则,溢出发生在不同的点 . 使用round-towards-infinity(向上舍入),将任何正值(即2-149)添加到2128-2104将导致溢出 . 舍入为零时,添加任何小于2104到2128-2104的值都不会溢出 .
是,并且当时的舍入模式处于活动状态 .
考虑
x before max
和FLT_MAX
之间的步骤 .最大的
float
大约是float
的两倍,同样的最小float
具有相同的步骤或ULP . 可以想象这个较小的float
,其所有显式精度位清零,而设置为FLOAT_MAX
.现在将其与
FLT_EPSILON
进行比较,这是从1.0到下一个更大的float
的最小步骤:请注意比率
delta/m0
是FLT_EPSILON
.考虑舍入到最近的典型舍入模式,与偶数的关系 .
现在让我们尝试将
1/2*delta1
添加到FLOAT_MAX
,然后尝试添加下一个较小的float
.如果约
FLT_MAX*1/2*1/2*FLOAT_EPSILON
,我们可以看到最小的delta .鉴于
float
的各种可能编码,您的结果可能会有所不同,但这种方法可以确定如何确定导致溢出的最小增量 .运行这个程序足够长的时间,看看会发生什么:
我想它会回答你的问题 .