在我开始之前,只是一些背景信息:
我正在ARM7微控制器(754 2294/01)上运行一个裸金属应用程序,该应用程序是在Keil uVision3中编译的,使用编译器标准的数学库(符合IEEE-754标准)。
问题:,我很难理解在两个单精度浮点输入之和上,到底是什么构成了“溢出”。
最初,我的印象是,如果我试图向可以用IEEE-754表示法表示的最大值中添加任何正值,则结果将生成溢出异常。
例如,假设我有:
a = 0x7f7fffff (ie. 3.4028235..E38);
b = 0x3f800000 (ie. 1.0)我预计,将这两个值相加将导致IEEE-754中定义的溢出。令我惊讶的是,结果只是返回'a‘的值,无一例外地被标记。
所以我想,由于精度(或者分辨率,如果你喜欢的话)随着被表示的值的增加而降低,在这种情况下,它的值'1‘很可能被有效地四舍五入到0,因为它是相对不重要的。
因此,提出了一个问题:,在这种情况下,最小的'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;
}是否有一条普遍适用的规则,以便能够提前知道(如。如果不执行sum),那么给定两个浮点数,它们的和将导致IEEE-754定义的溢出?
发布于 2018-08-24 05:56:28
它是否取决于IEEE-754的具体实现?
是的,并且舍入模式在当时是活动的。
考虑x before max和FLT_MAX之间的步骤。
float max = FLT_MAX;
float before_max = nextafterf(max, 0.0f);
float delta = max - before_max;
printf("max: %- 20a %.*g\n", max, FLT_DECIMAL_DIG, max);
printf("1st d: % -20a %.*g\n", delta, FLT_DECIMAL_DIG, delta);
// Typical output
max: 0x1.fffffep+127 3.40282347e+38
b4max: 0x1.fffffep+127 3.40282347e+38
1st d: 0x1p+104 2.02824096e+31最大的float大约是float的两倍,具有相同最小的float和相同的步骤,或者是ULP。想想这个更小的float,它的所有显式精度位都被清除了,与FLOAT_MAX一样。
float m0 = nextafterf(max/2, max);
printf("m0: %- 20a %.*g\n", m0, FLT_DECIMAL_DIG, m0);
// m0: 0x1p+127 1.70141183e+38现在将其与FLT_EPSILON进行比较,这是从1.0到下一个更大的float的最小步骤
float eps = FLT_EPSILON;
printf("epsil: %- 20a %.*g\n", eps, FLT_DECIMAL_DIG, eps);
// Output
// epsil: 0x1p-23 1.1920929e-07注意,比率delta/m0是FLT_EPSILON。
float r = delta1/m0;
printf("r: %- 20a %.*g\n", r, FLT_DECIMAL_DIG, r);
// r: 0x1p-23 1.1920929e-07考虑典型的四舍五入方式,舍入到最近,联系到偶数。
现在让我们尝试将1/2*delta1添加到FLOAT_MAX中,然后尝试添加下一个较小的float。
sum = max + delta1/2;
printf("sum: % -20a %.*g\n", sum, FLT_DECIMAL_DIG, sum);
sum = nextafterf(sum, 0);
printf("sum: % -20a %.*g\n", sum, FLT_DECIMAL_DIG, sum);
// sum: inf inf
// sum: 0x1.fffffep+127 3.40282347e+38IEEE-754:“最小”溢出条件
如果是关于FLT_MAX*1/2*1/2*FLOAT_EPSILON,我们可以看到最小的增量。
float small = FLT_MAX*0.25f*FLT_EPSILON;
printf("small: %- 20a %.*g\n", small, FLT_DECIMAL_DIG, small);
printf("sum: % -20a %.*g\n", max+small, FLT_DECIMAL_DIG, max+small);
small = nextafterf(small, max);
printf("sum: % -20a %.*g\n", max+small, FLT_DECIMAL_DIG, max+small);
// sum: 0x1.fffffep+127 3.40282347e+38
// sum: inf inf考虑到float的各种可能编码,您的结果可能有所不同,但是这种方法给出了如何确定导致溢出的最小增量的概念。
发布于 2018-08-23 22:19:52
运行这个程序足够长的时间,看看会发生什么:
float x = 10000000.0f;
while(1)
{
printf("%f\n", x);
x += 1.0f;
}我想它会回答你的问题。
https://stackoverflow.com/questions/51994695
复制相似问题