为什么十进制小数不能在二进制中被精确表示?

314
在SO上发布了几个关于浮点表示的问题,例如小数0.1没有精确的二进制表示,因此使用“==”运算符将其与另一个浮点数进行比较是危险的。我理解浮点表示背后的原理。但我不明白的是,从数学角度讲,为什么小数点右侧的数字比左侧的数字更加“特殊”?例如,数字61.0具有精确的二进制表示,因为任何数字的整数部分始终是精确的。但数字6.10就不是精确的了。我只是把小数点向右移动了一位,突然间从精确到不精确。在数学上,这两个数字之间应该没有内在的区别--它们只是数字而已。相反,如果我向左移动小数点一位,得到数字610,我仍然处于精确状态。我可以一直朝着这个方向走(6100、610000000、610000000000000),它们仍然是精确的。但是一旦小数点超过某个阈值,数字就不再精确了。发生了什么?编辑:为了澄清,我想避免讨论诸如IEEE等行业标准表示法,并坚持我认为的数学“纯粹”方式。 在十进制中,位值是:
... 1000  100   10    1   1/10  1/100 ...

在二进制中,它们将会是:

... 8    4    2    1    1/2  1/4  1/8 ...

这些数字也没有任何限制。数字的位置从左到右无限增加。


2
你可能会发现这篇文章有助于理解浮点数内部的运作方式:浮点数的解剖 - John D. Cook
61
在二进制中,数字3表示为2¹+2°=2+1。非常简单易懂。现在,看一下1/3。你如何使用负幂次的2来表示它?试试实验,你会发现1/3等于无限序列2^-2 + 2^-4 + 2^-6 + 2^-8 + ...的总和,也就是说,在二进制中很难精确地表示。 - Lars Haugseth
26
Jon Skeet在你的问题中提供了很好的回答。但有一件事情被遗漏了,那就是你实际上问了两个不同的问题。标题问题是“为什么十进制小数不能在二进制中精确表示?”答案是,它们可以被表示。在你的标题和正文之间,你混淆了“二进制”和“浮点表示”的概念。浮点数是一种以固定数量的二进制数字表示十进制数的方法,但会牺牲精度。而二进制只是另一种计数法,可以表示任何十进制数,只要有无限多的数字。 - Chris Blackwell
3
有一些系统具有精确的十进制表示,其工作方式基本上与您描述的相同。 SQL十进制类型就是一个例子。LISP语言内置了这个功能。有几个商用和开源库可用于使用精确的小数计算。只是目前还没有此类硬件支持,大部分语言和硬件都实现了IEEE标准,以32或64位表示无限数量的数字。 - nos
2
该问题似乎与数学有关(即使它是涉及编程的数学),更适合在 [math.se] 上进行讨论。 - Cole Tobin
22个回答

0

我不想重复其他20个答案已经总结的内容,所以我会简要回答:

你问题的答案:

为什么二进制数不能准确地表示某些比率?

与十进制小数无法准确表示某些比率的原因相同,即分母包含除2或5之外的质因数的既约分数始终在其小数展开的尾数中具有无限的字符串。

为什么二进制不能准确地表示十进制数?

这个问题表面上基于对值本身的误解。没有任何数字系统足以以一种使事物本身告诉您它既是数量,同时也在本身内部给出关于表示的内在价值的解释的方式来表示任何数量或比率。因此,所有数量表示和模型都是符号性的,并且只能在事后被理解,即在人们学习如何阅读和解释这些数字之后。

由于模型是主观的事物,只有反映现实才是真实的,因此我们不需要严格将二进制字符串解释为负数和正数幂的总和。相反,我们可以观察到,我们可以创建一个任意集合的符号,使用二进制或任何其他基数来精确表示任何数字或比率。只需考虑到我们可以用单个词甚至单个符号引用所有无限大的概念而不必“显示无限大”本身。

例如,我正在设计一种二进制编码以表示混合数,以便比IEEE 754浮点数具有更高的精度和准确性。在撰写本文时,我的想法是有一个符号位、一个倒数位、一定数量的标量位来确定如何“放大”小数部分,然后剩余的位数均匀分配给混合数的整数部分和后面的固定小数点数。如果设置了倒数位,则应将其解释为该数的倒数。这样做的好处是,我可以使用它们的倒数来表示具有无限小数扩展的数字,这些倒数确实具有终止的小数扩展,或者作为直接的分数,根据我的需求可能是近似值。


0
简单来说:计算机没有无限的内存来存储小数(在将十进制数表示为科学计数法形式后)。根据IEEE 754双精度浮点数标准,我们只有53位来存储小数。
更多信息请参考:http://mathcenter.oxford.emory.edu/site/cs170/ieee754/

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接