... 1000 100 10 1 1/10 1/100 ...
在二进制中,它们将会是:
... 8 4 2 1 1/2 1/4 1/8 ...
这些数字也没有任何限制。数字的位置从左到右无限增加。
... 1000 100 10 1 1/10 1/100 ...
在二进制中,它们将会是:
... 8 4 2 1 1/2 1/4 1/8 ...
这些数字也没有任何限制。数字的位置从左到右无限增加。
如果你有足够的空间,十进制数可以被精确地表示 - 只是不能用浮点二进制小数来表示。如果使用浮点十进制点类型(例如.NET中的System.Decimal),那么许多在二进制浮点中无法精确表示的值可以精确表示。
换个角度看问题 - 在你熟悉的十进制系统中,你无法精确表示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。将其与另一种情况进行比较:给定任意数量的十进制数字,您可以精确表示任何可以作为浮点二进制点的确切表示的数字。b
下,哪些数字具有有限的表示,哪些数字没有?仔细思考一下,我们可以知道一个数字x
有一个有限的b
表示,当且仅当存在一个整数n
,使得x b^n
是一个整数。x=11/500
具有一个有限的10表示,因为我们可以选择n=3
,然后x b^n=22
,是一个整数。然而,数字x=1/3
没有,因为无论我们选择什么样的n
,都无法摆脱3。x=p/q
(假设已经化简为最简形式),我们可以通过比较b
和q
的质因数分解来回答这个问题。如果q
有任何不在b
的质因数分解中的质因子,我们将永远无法找到一个合适的n
来摆脱这些因子。p/q
,其中q
有除2或5以外的质因子,都不会有有限的表示。所以现在回到十进制和二进制的基础上,我们可以看出任何有限十进制表示的有理数都是p/q
的形式,当且仅当 q
的质因数分解只包含2
和5
;同样地,这个数在二进制中有限表示当且仅当q
的质因子分解只包含2
。
但是其中一个情况是另一个情况的子集! 每当
q
的质因数分解只包含2
显然也成立:
q
的质因数分解只包含2
和5
或者说,每当p/q
在二进制下有限表示时,在十进制下p/q
也一定有限表示。 然而反过来却不成立-每当q
的质因数分解中有一个5
,它将具有有限的十进制表示,但没有有限的二进制表示。 这就是其他答案提到的0.1
例子。
所以我们得到了你问题的答案-因为2的质因数是10的质因数的子集,所有以2为终止的数字都是以10为终止的数字,但反之则不成立。 这与61和6.1无关-而是10和2的关系。
最后说明一下,如果由于某种怪癖人们使用(比如)17进制而我们的计算机使用5进制,你的直觉永远不会被误导 - 没有(非零,非整数)同时在这两种情况下具有有限表示的数字!
x
是否具有终止的 b
-表示。 - Jingguo Yao根本(数学)原因在于,当您处理整数时,它们是可数无限的。
这意味着,即使有无限多个整数,我们也可以“数出”序列中的所有项,而不会跳过任何一项。这意味着如果我们想要获取列表中第610000000000000
个位置上的项,我们可以通过公式计算出来。
然而,实数是不可数无限的。你不能说“给我第610000000000000
个位置上的实数”,并得到一个答案。原因是因为,在考虑浮点值时,在0
和1
之间甚至存在无限数量的值。对于任何两个浮点数,情况也是如此。
更多信息:
http://en.wikipedia.org/wiki/Countable_set
http://en.wikipedia.org/wiki/Uncountable_set
更新:非常抱歉,我似乎误解了问题。我的回答是关于为什么我们不能表示每个真实值,我没有意识到浮点数会自动归类为有理数。
Hehner引号表示法 是一种做到这一点的方法。他使用引号符号来表示序列的重复部分。文章:http://www.cs.toronto.edu/~hehner/ratno.pdf 和维基百科页面:http://en.wikipedia.org/wiki/Quote_notation。
没有什么规定我们不能向表示系统添加符号,所以我们可以使用二进制引号表示法精确地表示十进制比率,反之亦然。
BCD - 二进制编码十进制 - 表示法是精确的。虽然它们不太能节省空间,但在这种情况下,这是您必须做出的权衡。
(注:我会在这里附加“ b”以表示二进制数字。所有其他数字均以十进制给出)
一种思考方式是使用类似科学计数法的方法。我们习惯于看到用科学计数法表示的数字,例如6.022141 * 10^23。浮点数使用类似的格式-尾数和指数来存储内部数据,但使用2的幂而不是10的幂。
您的61.0可以重写为1.90625 * 2^5,或者使用尾数和指数将其重写为1.11101b * 2^101b。要将其乘以十并(移动小数点),我们可以执行以下操作:
(1.90625 * 2^5)*(1.25 * 2^3)=(2.3828125 * 2^8)=(1.19140625 * 2^9)
或者使用二进制中的尾数和指数:
(1.11101b * 2^101b)*(1.01b * 2^11b)=(10.0110001b * 2^1000b)=(1.00110001b * 2^1001b)
请注意我们如何乘以这些数字。我们乘以尾数并添加指数。然后,由于尾数大于二,我们通过提高指数来规范化结果。这就像我们在十进制科学计数法中对数字进行操作后调整指数一样。在每种情况下,我们使用二进制中有限的表示来处理值,因此基本乘法和加法运算产生的输出值也会产生具有有限表示的值。
现在,考虑如何将61除以10。我们将从除以尾数1.90625和1.25开始。在十进制中,这会给出1.525,一个不错的短数字。但是,如果我们将其转换为二进制,它是多少呢?我们将按照通常的方式进行-尽可能减去最大的2的幂,就像将整数十进制转换为二进制一样,但我们将使用负2的幂:
1.525 - 1*2^0 --> 1 0.525 - 1*2^-1 --> 1 0.025 - 0*2^-2 --> 0 0.025 - 0*2^-3 --> 0 0.025 - 0*2^-4 --> 0 0.025 - 0*2^-5 --> 0 0.025 - 1*2^-6 --> 1 0.009375 - 1*2^-7 --> 1 0.0015625 - 0*2^-8 --> 0 0.0015625 - 0*2^-9 --> 0 0.0015625 - 1*2^-10 --> 1 0.0005859375 - 1*2^-11 --> 1 0.00009765625...
哎呀,现在我们有麻烦了。当用二进制表示1.90625/1.25=1.525时,它是一个重复的小数:1.11101b/1.01b=1.10000110011...b。我们的计算机只能存储有限的位数来保存尾数,并且会将该小数四舍五入并假定某个点后面全是零。当你将61除以10时看到的误差就是:1.100001100110011001100110011001100110011...b * 2^10b
和:
1.100001100110011001100110b * 2^10b正是因为尾数的四舍五入导致了我们与浮点值相关的精度损失。即使尾数可以准确地表示(例如,仅添加两个数字),如果尾数需要太多位才能适应归一化指数,则仍然可能出现数字损失。
事实上,当我们将十进制数字舍入到可管理的大小并只给出前几个数字时,我们经常这样做。因为我们用十进制表示结果,所以感觉很自然。但是如果我们将一个小数舍入并将其转换为不同的基数,那么它看起来就像由于浮点舍入而得到的小数一样丑陋。
这是一个好问题。
你的问题都基于“我们如何表示数字?”
所有数字都可以用十进制表示或二进制(2的补码)表示。 所有数字!!
但是一些数字(其中大多数)需要无限数量的元素(对于二进制位置,“0”或“1”,或对于十进制表示,“0”到“9”)。
比如十进制表示中的1/3(1/3 = 0.3333333... <- 有无限数量的“3”)
比如二进制中的0.1(0.1 = 0.00011001100110011.... <- 有无限数量的“0011”)
一切都在这个概念中。由于您的计算机只能考虑有限的数字集合(十进制或二进制),因此只有一些数字可以在您的计算机中被准确地表示...
正如Jon所说,3是一个质数,不是10的因子,因此1/3不能用基数为10的有限元素表示。
即使使用任意精度算术,基于2的编号位置系统也无法完全描述6.1,尽管它可以表示61。
对于6.1,我们必须使用另一种表示方法(例如十进制表示或IEEE 854,它允许使用基数2或基数10表示浮点值)
这是因为在十进制中,你无法精确地表示1/3,需要写成0.33333(3)的形式。在二进制中也是同样的问题,只不过出现在不同的数字集合中。
0; 3
5/9 (0.5555...)
0; 1, 1, 4
10/43 (0.232558139534883720930...)
0; 4, 3, 3
9093/18478 (0.49209871198181621387596060179673...)
在这里显示了一个比例,表示为分数和小数形式。0; 2, 31, 7, 8, 5
从这里开始,有许多已知的方法可以在内存中存储一系列整数。
除了完全准确地存储您的数字外,连分数还具有其他一些好处,例如最佳有理逼近。如果您决定提前终止连分数的数字序列,则剩余的数字(重新组合为一个分数)将给出最佳可能的分数。这就是找到 pi 的近似值的方法:
圆周率的连分数:
3; 7, 15, 1, 292 ...
将序列在1处终止,得到分数:
355/113
这是一个非常好的有理数近似。