为什么十进制数不能用二进制精确表示?

问题描述

如果您有足够的空间,十进制数 可以 精确表示 - 只是不能用浮点 二进制 数表示。如果您使用浮点 小数点 类型(例如System.Decimal在.NET 中),则可以精确表示大量无法以二进制浮点数精确表示的值。

让我们以另一种方式看待它 - 在您可能习惯的以 10 为底的情况下,您无法准确表达 1/3。它是 0.3333333…(重复出现)。您不能将 0.1 表示为二进制浮点数的原因完全相同。您可以精确地表示 3、9 和 27 - 但不能表示 1/3、1/9 或 1/27。

问题是 3 是一个质数,它不是 10 的因数。当您想将一个数字 乘以 3 时,这不是问题:您始终可以乘以一个整数而不会遇到问题。但是当你 除以 一个素数并且不是你的基数时,你可能会遇到麻烦(如果你试图将 1 除以那个数字,就会遇到麻烦)

虽然 0.1 通常用作无法用二进制浮点精确表示的精确十进制数的最简单示例,但可以说 0.2 是一个更简单的示例,因为它是 1/5 - 而 5 是导致十进制和二进制之间问题的质数.


处理有限表示问题的旁注:

一些浮点小数点类型具有固定大小,就像System.Decimal其他java.math.BigDecimal“任意大”一样 - 但它们会在某个时候达到限制,无论是系统内存还是数组的理论最大大小。但是,这与该答案的主要内容完全不同。即使您有真正任意大量的位可供使用,您仍然无法用浮点二进制点表示精确地表示十进制 0.1。将其与相反的方式进行比较:给定任意数量的十进制数字,您 可以 精确地表示任何可以精确表示为浮点二进制点的数字。

解决方法

已经向 SO 发布了几个关于浮点表示的问题。例如,十进制数 0.1 没有精确的二进制表示,因此使用 ==
运算符将其与另一个浮点数进行比较是很危险的。我了解浮点表示背后的原理。

我不明白的是,从数学的角度来看,为什么小数点右边的数字比左边的数字更“特殊”?

例如,数字 61.0 具有精确的二进制表示,因为任何数字的整数部分总是精确的。但数字 6.10 并不准确。我所做的只是将小数点移动一位,然后我突然从
Exactopia 转到了 Inexactville。从数学上讲,这两个数字之间应该没有本质的区别——它们只是数字。

相比之下,如果我将小数点向另一个方向移动一位以产生数字 610,我仍然在
Exactopia。我可以继续朝那个方向前进(6100、610000000、610000000000000),它们仍然是准确的、准确的、准确的。但一旦小数点超过某个阈值,数字就不再准确。

这是怎么回事?

编辑:澄清一下,我想远离关于行业标准表示的讨论,例如 IEEE,并坚持我认为是数学上“纯”的方式。以 10 为底,位置值为:

... 1000  100   10    1   1/10  1/100 ...

在二进制中,它们将是:

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

这些数字也没有任意限制。位置向左和向右无限增加。