第67章中的一个例子

A example in Gotw 67

本文关键字:一个 67章      更新时间:2023-10-16

在http://www.gotw.ca/gotw/067.htm

中有一个例子
int main()
{
  double x = 1e8;
  //float x = 1e8;
  while( x > 0 )
  {
    --x;
  }
}

当你改变双精度为float时,它在VS2008中是一个无限循环。根据Gotw的解释:

如果float不能精确地表示从0到的所有整数值怎么办1 e8 ?然后修改后的程序会开始倒数,但是最终达到一个无法表示的值NN-1 == N(由于浮点精度不足)…然后循环将一直停留在该值上,直到执行该操作的机器

程序正在运行

据我所知,IEEE754浮点数是单精度(32位),浮点数的范围应该是+/- 3.4e +/- 38,它应该有7位有效数字。

但我仍然不明白这到底是怎么发生的:"最终到达一个无法表示的值N,并且N-1 == N(由于浮点精度不足)。"有人能解释一下吗?

一点额外的信息:当我使用double x = 1e8时,它在大约1秒内完成,当我把它改成浮动x = 1e8,它运行的时间更长(仍然运行5分钟后),如果我把它改为float x = 1e7;,它在大约1秒内完成。

我的测试环境是VS2008。

顺便说一句,我是而不是询问基本的IEEE 754格式解释,因为我已经理解了。

谢谢

为了便于讨论,让我们假设我们有一个处理器,它表示一个有7位有效十进制数字的浮点数,以及一个有2位十进制数字的尾数。那么现在数字1e8将被存储为

1.000 000 e 08

(其中"。"answers"e"不需要实际存储)

现在你想计算"1e8 - 1"。1表示为

1.000 000 e 00

现在,为了进行减法,我们首先以无限精度进行减法,然后规范化,使"。"之前的第一个数字在1和9之间,最后四舍五入到最接近的可表示值(例如偶数)。"1e8 - 1"的无限精度结果为

0.99 999 999 e 08

或归一化

9.9 999 999 e 07

可以看出,无限精度的结果需要比我们的体系结构实际提供的有效位多一位;因此,我们需要将无限精确的结果四舍五入(并重新规范化)为7位有效数字,从而产生

1.000 000 e 08

因此你以"1e8 - 1 == 1e8"结束,你的循环永远不会终止。

现在,实际上您使用的是IEEE 754二进制浮点数,它们有点不同,但原理大致相同。

操作x--(在本例中)等同于x = x - 1。这意味着取x的原始值,减去1(按照IEEE 754-1985的要求,使用无限精度),然后将结果四舍五入到float值空间的下一个值。

对于i in [-10;10], 1.0e8f + i的四舍五入结果如下:

 -10: 9.9999992E7     (binary +|10011001|01111101011110000011111)
  -9: 9.9999992E7     (binary +|10011001|01111101011110000011111)
  -8: 9.9999992E7     (binary +|10011001|01111101011110000011111)
  -7: 9.9999992E7     (binary +|10011001|01111101011110000011111)
  -6: 9.9999992E7     (binary +|10011001|01111101011110000011111)
  -5: 9.9999992E7     (binary +|10011001|01111101011110000011111)
  -4: 1.0E8           (binary +|10011001|01111101011110000100000)
  -3: 1.0E8           (binary +|10011001|01111101011110000100000)
  -2: 1.0E8           (binary +|10011001|01111101011110000100000)
  -1: 1.0E8           (binary +|10011001|01111101011110000100000)
   0: 1.0E8           (binary +|10011001|01111101011110000100000)
   1: 1.0E8           (binary +|10011001|01111101011110000100000)
   2: 1.0E8           (binary +|10011001|01111101011110000100000)
   3: 1.0E8           (binary +|10011001|01111101011110000100000)
   4: 1.0E8           (binary +|10011001|01111101011110000100000)
   5: 1.00000008E8    (binary +|10011001|01111101011110000100001)
   6: 1.00000008E8    (binary +|10011001|01111101011110000100001)
   7: 1.00000008E8    (binary +|10011001|01111101011110000100001)
   8: 1.00000008E8    (binary +|10011001|01111101011110000100001)
   9: 1.00000008E8    (binary +|10011001|01111101011110000100001)
  10: 1.00000008E8    (binary +|10011001|01111101011110000100001)

所以你可以看到1.0e8f1.0e8f + 4和其他一些数字有相同的表示。因为您已经知道IEEE 754-1985浮点格式的细节,所以您也知道剩下的数字必须被四舍五入。

如果n - 1n由于浮点数的近似性质而具有相同的表示,那么n的结果是什么?

关于"到达"一个无法表示的值,我认为Herb包含了相当深奥的浮点表示的可能性。

对于任何普通的浮点表示法,您将从这样的值开始(即停留在第一个值上),或者您将在可以精确表示的以零为中心的连续整数范围内的某个地方,以便倒计时成功。

对于IEEE 754, 32位表示,通常是c++中的float,有23位尾数,而64位表示,通常是c++中的double,有52位尾数。这意味着使用double至少可以精确地表示-(2^52-1)范围内的整数…2 ^ 52-1。我不太确定这个范围是否可以再扩大2倍。一想到这个我就有点头晕。: -)

干杯,hth。