有几个关于浮点表示的问题发布到了 SO。例如,十进制数 0.1 没有精确的二进制表示形式,因此使用 == 运算符将其与另一个 float 进行比较是危险的。我了解浮点表示背后的原理。
我不明白的是,为什么从数学角度来看,小数点右边的数字比左边的数字更“特殊”?
例如,数字 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 ...
这些数字也没有任意限制。位置向左和向右无限增加。
最佳答案
如果有足够的空间,十进制数可以精确表示 - 只是不能用 float 二进制点数表示。如果您使用浮点小数点类型(例如.NET中的System.Decimal
),那么许多无法用二进制浮点精确表示的值都可以精确表示.
让我们以另一种方式看待它 - 在您可能熟悉的 10 进制中,您无法准确地表达 1/3。它是 0.3333333...(重复出现)。不能将 0.1 表示为二进制 float 的原因完全相同。您可以准确地表示 3、9 和 27,但不能表示 1/3、1/9 或 1/27。
问题是 3 是一个质数,它不是 10 的因数。当您想要将一个数乘 3 时,这不是问题:您始终可以乘以一个整数,而无需遇到问题。但是,当您除一个素数且不是基数的因数时,您可能会遇到麻烦(如果您尝试除 1将会)按这个数字)。
虽然 0.1 通常用作精确十进制数的最简单示例,无法用二进制 float 精确表示,但可以说 0.2 是一个更简单的示例,因为它是 1/5 - 而 5 是导致问题的质数十进制和二进制。
<小时/>处理有限表示问题的旁注:
一些 float 小数点类型具有固定大小,如 System.Decimal,其他类型如 java.math.BigDecimal 是“任意大” - 但它们会达到限制在某些时候,无论是系统内存还是数组的理论最大大小。然而,这与这个答案的主要观点完全不同。即使您有真正任意数量的位数可供使用,您仍然无法以浮点二进制点表示形式精确地表示十进制 0.1。与相反的方式进行比较:给定任意数量的十进制数字,您可以精确地表示任何可以精确表示为 float 二进制小数点的数字。
关于math - 为什么十进制数不能用二进制精确表示?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46942191/