因为不是每个数字都可以用 IEEE754 浮点值表示。在某些时候,您会得到一个不太具有代表性的数字,计算机将不得不选择最接近的数字。
如果您输入 0.05Harald Schmidt's excellent online converter
并参考IEEE754-1985 上的维基百科条目,您将得到以下位(我的解释如下):
s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm
0 01111010 10011001100110011001101
|||||||| |||||||||||||||||||||||
128 -+||||||| ||||||||||||||||||||||+- 1 / 8388608
64 --+|||||| |||||||||||||||||||||+-- 1 / 4194304
32 ---+||||| ||||||||||||||||||||+--- 1 / 2097152
16 ----+|||| |||||||||||||||||||+---- 1 / 1048576
8 -----+||| ||||||||||||||||||+----- 1 / 524288
4 ------+|| |||||||||||||||||+------ 1 / 262144
2 -------+| ||||||||||||||||+------- 1 / 131072
1 --------+ |||||||||||||||+-------- 1 / 65536
||||||||||||||+--------- 1 / 32768
|||||||||||||+---------- 1 / 16384
||||||||||||+----------- 1 / 8192
|||||||||||+------------ 1 / 4096
||||||||||+------------- 1 / 2048
|||||||||+-------------- 1 / 1024
||||||||+--------------- 1 / 512
|||||||+---------------- 1 / 256
||||||+----------------- 1 / 128
|||||+------------------ 1 / 64
||||+------------------- 1 / 32
|||+-------------------- 1 / 16
||+--------------------- 1 / 8
|+---------------------- 1 / 4
+----------------------- 1 / 2
符号为 0,为正。指数由映射到左侧数字的一位表示:64+32+16+8+2 = 122 - 127 bias = -5
,因此乘数为 2 -5或1/32
。偏差是允许表示非常小的127
数字(如接近零而不是大的负数)。
尾数稍微复杂一些。对于每一个位,您在右侧累积数字(在添加隐含之后1
)。因此,您可以将数字计算为 的总和{1, 1/2, 1/16, 1/32, 1/256, 1/512, 1/4096, 1/8192, 1/65536, 1/131072, 1/1048576, 1/2097152, 1/8388608}
。
当你把所有这些加起来时,你得到1.60000002384185791015625
.
当您将其乘以乘数1/32
(之前从指数位计算)时,您会得到0.0500000001
,因此您可以看到它0.05
已经没有精确表示。尾数的这个位模式实际上是相同的,0.1
但是,指数是-4而不是-5,这就是为什么0.1 + 0.1 + 0.1
很少等于0.3
(这似乎是一个最喜欢的面试问题)。
当你开始把它们加起来时,那个小错误会累积起来,因为你不仅会看到它本身的错误,0.05
而且在累积的每个阶段也可能会引入错误——并不是所有的数字0.1
,等等都可以表示出来确切地说。0.15
0.2
最终,如果您使用默认精度,错误将变得足够大,以至于它们将开始显示在数字中。您可以通过以下方式选择自己的精度来推迟此操作:
#include <iostream>
#include <iomanip>
:
std::cout << std::setprecison (2) << time << '\n';
它不会修复变量值,但会在错误变得可见之前给您更多的喘息空间。
顺便说一句,有些人建议避免,std::endl
因为它会强制刷新缓冲区。如果您的实现自己运行,那么当您发送换行符时,终端设备就会发生这种情况。而且,如果您已将标准输出重定向到非终端,您可能不希望在每一行上都刷新。与您的问题并不真正相关,并且在绝大多数情况下可能不会产生真正的影响,这只是我想我会提出的一点。