C++ Gotw 67中的一个示例

C++ Gotw 67中的一个示例,c++,floating-point,precision,gotw,C++,Floating Point,Precision,Gotw,这方面有一个例子 当您将double更改为float时,它在VS2008中是一个无限循环。 根据Gotw的解释: 如果float不能准确地表示从0到0的所有整数值,该怎么办 1e8?然后修改后的程序将开始倒计时,但 最终达到一个无法表示的值N,并且 N-1==N(由于浮点精度不足)。。。然后 循环将一直停留在该值上,直到 程序正在运行,电源耗尽 据我所知,IEEE754浮点为单精度(32位),浮点范围应为+/-3.4e+/-38,有效位数应为7位 但我仍然不明白这到底是怎么发生的:“最终达到一个

这方面有一个例子

当您将double更改为float时,它在VS2008中是一个无限循环。 根据Gotw的解释:

如果float不能准确地表示从0到0的所有整数值,该怎么办 1e8?然后修改后的程序将开始倒计时,但 最终达到一个无法表示的值N,并且 N-1==N(由于浮点精度不足)。。。然后 循环将一直停留在该值上,直到 程序正在运行,电源耗尽

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

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

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

我的测试环境是VS2008

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


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

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

对于任何普通的浮点表示法,您要么从该值开始(即停留在第一个值上),要么在以零为中心的连续整数范围内的某个位置,可以精确表示,以便倒计时成功

对于IEEE 754,32位表示,通常是C++中的代码>浮点< /C> >,有23位尾数,而C++中的64位表示,通常是代码>双,有52位尾数。这意味着使用
double
至少可以精确表示范围-(2^52-1)中的整数。。。2^52-1. 我不太确定这个范围是否可以用另一个因子2来扩展。想起来我有点头晕。:-)


干杯,

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

1.000 000 e 08
(其中,“.”和“e”不需要实际存储。)

现在你要计算“1e8-1”。1表示为

1.000 000 e 00
现在,为了进行减法,我们首先以无限精度进行减法,然后进行规格化,使“.”之前的第一个数字介于1和9之间,最后四舍五入到最接近的可表示值(例如,收支平衡)。“1e8-1”的无限精度结果为

还是正常化

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的规定,使用无限精度),然后将结果四舍五入到
浮点值空间的下一个值

以下[-10;10]
中的
i给出了数字
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.0e8f
1.0e8f+4
以及其他一些数字具有相同的表示形式。既然您已经知道IEEE 754-1985浮点格式的详细信息,那么您也知道剩余的数字必须已被舍入。

这并没有回答他的问题“这到底是如何发生的”[sic]。也就是说,n-1==n怎么可能。你的答案是关于浮点精度的一般答案,而不是这个特定的问题…@巴特:它确实回答了你指出的问题,但不是OP提出的“最终到达”的问题。确实很难想象“最终到达”是有意义的。我想我会称这种说法为“反常的”:-@Alf这是我的观点,尽管措辞可能很糟糕。Thanks@Francesco:形式数学推理基于鸽子洞原理。FLOAT_MAX>10^38,因此有超过10^38个正整数1.000 000 e 08
 -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)