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

有几个关于浮点表示法的问题被提交给了SO。例如,十进制数0.1没有精确的二进制表示,因此使用==操作符将其与另一个浮点数进行比较是危险的。我理解浮点表示法背后的原理。

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

例如,数字61.0具有精确的二进制表示,因为任何数字的整数部分总是精确的。但6.10这个数字并不准确。我所做的只是把小数点移了一位突然间我就从精确乌托邦变成了不精确镇。从数学上讲,这两个数字之间不应该有本质差别——它们只是数字。

相比之下,如果我把小数点向另一个方向移动一位,得到数字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 ...

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

122220 次浏览

如果你有足够的空间,十进制数字可以可以精确地表示——只是不能用浮点的二进制点数表示。如果你使用浮点的小数点类型(例如。net中的System.Decimal),那么很多不能用二进制浮点精确表示的值都可以精确表示。

让我们从另一个角度来看——以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。与另一种方式进行比较:给定任意数量的十进制数字,可以精确地表示任何可以精确地表示为浮点二进制的数字。

如果你用浮点数做一个足够大的数(它可以做指数),那么小数点前也会不精确。所以我不认为你的问题是完全正确的,因为前提是错误的;移位10并不总是会产生更高的精度,因为在某些情况下,浮点数将不得不使用指数来表示数字的大小,这样也会失去一些精度。

BCD - 二-十进制计数法 -表示是精确的。它们不是很节省空间,但在这种情况下,这是为了准确性而必须做出的权衡。

问题是你并不知道这个数字是否真的是61.0。考虑一下:

以前< p > < > <代码> 浮动a = 60; 浮动b = 0.1; c = a + b * 10; < / pre > < /代码> < / p >

c的值是多少?它不是61,因为b不是。1因为。1不是精确的二进制表示。

根(数学)原因是,当你处理整数时,它们是可数无限

这意味着,即使它们的数量是无限的,我们也可以“数出”序列中的所有项目,而不会跳过任何一项。这意味着如果我们想要获得列表中__abc0位的项,我们可以通过公式计算出来。

然而,实数是不可数无限。你不能说“给我位置610000000000000的实数”并得到一个答案。原因是,即使在01之间,当你考虑浮点值时,也有无限个值。这同样适用于任何两个浮点数。

更多信息:

http://en.wikipedia.org/wiki/Countable_set

http://en.wikipedia.org/wiki/Uncountable_set

< >强更新: 很抱歉,我似乎误解了这个问题。我的回答是关于为什么我们不能表示每个真正的值,我没有意识到浮点数被自动分类为理性

数字61.0确实有一个精确的浮点运算——但对于所有整数不是这样的。如果您编写了一个循环,将一个双精度浮点数和一个64位整数都加了1,最终您将达到这样的情况:64位整数完美地表示一个数字,而浮点数却不能——因为没有足够的有效位。

只是在小数点右边求近似值要容易得多。如果你把所有的数字都写成二进制浮点数,这就更有意义了。

另一种思考的方式是,当你注意到61.0完全可以用10为底表示时,移动小数点并不会改变这一点,你是在执行10的幂乘法(10^1,10^-1)。在浮点数中,乘以2的幂并不影响数字的精度。试着用61.0反复除以3来说明一个非常精确的数字是如何失去它的精确表示的。

这和你不能精确地以10为基数表示1/3的原因是一样的,你需要说0.33333(3)。在二进制中,这是相同类型的问题,只是发生在不同的数字集上。

有理数的数量是无限的,而用来表示有理数的比特的数量是有限的。看到http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems

有一个阈值,因为数字的含义已经从整数变成了非整数。要表示61,有6*10^1 + 1*10^0;10^1和10^0都是整数。6.1是6*10^0 + 1*10^-1,但10^-1是1/10,显然不是整数。这就是你在不精确镇的下场。

分数和整数可以构成比例尺。有些分数,如1/7,如果没有很多很多的小数,就不能用十进制形式表示。因为浮点数是基于二进制的,所以特殊情况会发生变化,但同样的精度问题也会出现。

你们知道整数,对吧?每一位代表2^n

< p > < br > 2 ^ 4 = 16
2 ^ 3 = 8
2 ^ 2 = 4
2 ^ 1 = 2
2 ^ 0 = 1 < / p >

对于浮点数来说是一样的(有一些区别),但是比特代表2^-n 2 ^ 1 = 1/2 = 0.5 < br > 2 ^ 2 = 1 / (2 * 2) = 0.25 < br > 2 ^ 3 = 0.125 < br > 2 ^ 4 = 0.0625 < br > < / p >

浮点二进制表示法:

符号 指数      分数(我认为无形的1被附加到分数)
B10 B9 b8 B6 B5 B4 B3 B2 B1 B0

(注意:我将在这里添加'b'来表示二进制数。其他数字均为十进制)

一种思考方法是用科学记数法。我们习惯看到用科学符号表示的数字,比如6.022141 * 10^23。浮点数内部使用类似的格式存储——尾数和指数,但使用2的幂而不是10。

你的61.0可以重写为1.90625 * 2^5,或者1.11101b * 2^101b加上尾数和指数。把它乘以10(移动小数点),我们可以这样做:

(1.90625 * 2 ^ 5) * (1.25 * 2 ^ 3) = (2.3828125 * 2 ^ 8) = (1.19140625 * 2 ^ 9)

或者在二进制中用尾数和指数:

(1.1110b * 2^101b) * (1.01b * 2^11b) = (10.011000b * 2^1000b) = (1.0011000b * 2^1001b)

注意我们是怎么相乘的。我们把尾数相乘,再把指数相加。然后,由于尾数的结尾大于2,我们通过碰撞指数来标准化结果。这就像我们在对十进制科学记数法中的数字进行运算后调整指数一样。在每种情况下,我们处理的值都有一个有限的二进制表示,因此通过基本的乘法和加法操作输出的值也产生了一个有限的表示。

现在,考虑一下我们如何用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.1110b / 1.01b = 1.10000110011…b我们的机器只有这么多位来容纳尾数,所以它们会四舍五入,假设超过某一点是零。当你用61除以10时,你看到的错误是:

< p > 1.100001100110011001100110011001100110011……B * 2^10b
,说:< br > 1.100001100110011001100110b * 2^10b

正是尾数的舍入导致了我们与浮点值相关的精度损失。即使当尾数可以精确地表示(例如,当只是两个数字相加时),如果在标准化指数后尾数需要太多数字来拟合,我们仍然会得到数字损失。

实际上,我们一直在做这样的事情,当我们把小数四舍五入到一个可管理的大小时,只给出它的前几位。因为我们用十进制表示结果,所以感觉很自然。但是如果我们四舍五入一个小数,然后把它转换成不同的底数,它看起来就像我们通过浮点四舍五入得到的小数一样难看。

这是个好问题。

你所有的问题都是基于“我们如何表示一个数字?”

所有的数字都可以用十进制表示,也可以用二进制(2的补码)表示。所有人!!

一些(大多数)需要无限数量的元素(“0”或“1”表示二进制位置,“0”,“1”到“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,我们必须使用另一种表示法(比如十进制表示法,或者允许以2为底或以10为底表示浮点值的IEEE 854)。

例如,数字61.0具有精确的二进制表示,因为任何数字的整数部分总是精确的。但6.10这个数字并不准确。我所做的只是把小数点移了一位突然间我就从精确乌托邦变成了不精确镇。从数学上讲,这两个数字之间不应该有本质差别——它们只是数字

让我们暂时撇开以10为底和以2为底的细节。让我们问一下,在base b中,哪些数字有终止表示,哪些数字没有?稍微思考一下就会告诉我们,当且仅当存在一个整数n,使得x b^n是一个整数时,数字x具有终止__abc0表示。

因此,例如,x = 11/500有一个终止10表示,因为我们可以选择n = 3x b^n = 22,一个整数。但是x = 1/3没有,因为无论n选择什么,我们都无法摆脱3。

第二个例子促使我们思考因子,我们可以看到,对于任何理性的 x = p/q(假设是最小值),我们可以通过比较bq的质因数分解来回答这个问题。如果q有任何质因数不在b的质因数分解中,我们将永远无法找到一个合适的n来摆脱这些因数。

因此,对于以10为基数的任何 p/q,其中q有除2或5之外的素数因子,将没有终止表示。

所以现在回到10和2进制,我们看到任何以10为终止表示的有理数都将是p/q的形式,恰好当q在质因数分解中只有__abc2和5s时;当q在质因数分解中只有__abc2时,相同的数将有一个终止2表示。

但其中一个案例是另一个案例的子集!每当

q在质因数分解中只有__abc1

显然是正确的

q在质因数分解中只有__abc1和__abc2

或者,换句话说,只要p/q有一个终止的2表示,p/q就有一个终止的10表示。然而,相反的也成立——只要q的质因数分解中有一个5,它就会有一个终止的10表示,而是一个终止的2表示。这是其他答案提到的0.1示例。

所以我们有了你问题的答案——因为2的质因数是10的质因数的子集,所以所有以2结尾的数都是以10结尾的数,反之则不然。这不是关于61对6.1的问题——这是关于10对2的问题。

作为结束注释,如果一些奇怪的人使用(比如说)以17为基数,而我们的计算机使用以5为基数,你的直觉永远不会被这引入歧途——在这两种情况下都会有没有(非零,非整数)数字终止!

上面的高分答案完全正确。

首先,你的问题中混合了以2为底和以10为底的数,然后当你把一个不能整除的数放在右边时,你就有问题了。比如十进制的1/3因为3不能整除10的幂,或者二进制的1/5不能整除2的幂。

另一个评论是永远不要对浮点数使用等号,句号。即使它是一个精确的表示,在一些浮点系统中,有些数字可以用不止一种方式准确表示(IEEE在这方面很糟糕,它是一个可怕的浮点规范,所以预计头疼)。这里也一样1/3不等于计算器上的数字0.3333333,不管小数点右边有多少个3。它是或可以足够接近,但不相等。所以你会期望2*1/3不等于2/3取决于四舍五入。永远不要对浮点数使用等号。

重复一下我在对Skeet先生的评论中所说的话:我们可以表示1/3、1/9、1/27或任何十进制的有理数。我们通过添加一个额外的符号来实现。例如,在数字的十进制展开中重复的数字上的一行。将十进制数表示为二进制数序列所需要的是1)(二进制数序列)、2)(基数点)和3)(表示该序列重复部分的其他符号)。

赫纳的引用符号是这样做的一种方式。他用引号表示序列中重复的部分。文章:http://www.cs.toronto.edu/~hehner/ratno.pdf和维基百科条目:http://en.wikipedia.org/wiki/Quote_notation

并没有说我们不能在表示系统中添加一个符号,所以我们可以用二进制引号表示十进制有理数,反之亦然。

我很惊讶没有人说过这一点:使用持续的分数。任何有理数都可以用二进制有限地表示。

一些例子:

1/3(0.3333…)

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

从这里开始,有多种已知的方法可以在内存中存储整数序列。

除了精确地存储数字外,连分式还有其他一些好处,比如最佳有理逼近。如果您决定提前终止连分式中的数字序列,则剩余的数字(当重新组合为分数时)将给出可能的最佳分数。这是如何找到圆周率的近似值的:

π的连分式:

3; 7, 15, 1, 292 ...

在1处终止序列,得到的分数是:

355/113

这是一个很好的有理近似。

正如我们一直在讨论的,在浮点算术中,十进制0.1不能完美地用二进制表示。

浮点和整数表示形式为所表示的数字提供网格或格子。当完成算术运算时,结果会从网格中脱落,必须通过舍入将其放回网格中。例如二进制网格上的1/10。

如果我们像一位先生建议的那样,使用二进制编码的十进制表示,我们能在网格上保持数字吗?

在等式中

2^x = y ;
x = log(y) / log(2)

因此,我想知道我们是否可以有一个二进制的对数制,

 2^1, 2^0, 2^(log(1/2) / log(2)), 2^(log(1/4) / log(2)), 2^(log(1/8) / log(2)),2^(log(1/16) / log(2)) ........

这也许能解决问题,所以如果你想把32.41写成二进制,那就是

2^5 + 2^(log(0.4) / log(2)) + 2^(log(0.01) / log(2))

2^5 + 2^(log(0.41) / log(2))
一个简单的答案:计算机没有无限的内存来存储分数(在以科学记数法的形式表示十进制数之后)。根据IEEE 754双精度浮点数标准,我们只有53位的限制来存储分数。 更多信息:http://mathcenter.oxford.emory.edu/site/cs170/ieee754/

我不想重复其他20个答案的总结,所以我只简单地回答:

答案在你的内容中:

为什么以两为基数的数字不能精确地表示一定的比率?

出于同样的原因,小数不足以表示某些比率,即分母中包含除2或5之外的素数因子的不可约分数,至少在其小数展开的尾数中总是有一个不确定的字符串。

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

从表面上看,这个问题是基于对价值观本身的误解。任何数字系统都不足以表示任何数量或比例,因为事物本身告诉你它既是一个数量,同时也给出了关于这种表示的内在价值的解释。因此,所有的定量表征和模型一般都是象征性的,只能在被教会如何阅读和解释这些数字之后才能理解。

由于模型是主观的东西,在反映现实的范围内是正确的,我们不需要严格地将二进制字符串解释为2的负幂和正幂的和。相反,我们可以观察到,我们可以创建一组任意的符号,这些符号以2为基底或任何其他基底来精确地表示任何数字或比例。只要考虑一下,我们可以用一个词,甚至一个符号来指代所有的无限,而不“显示无限”。本身。

作为一个例子,我正在为混合数字设计一个二进制编码,这样我可以有比IEEE 754浮点数更高的精度和准确性。在写这篇文章的时候,我们的想法是有一个符号位,一个倒数位,一个标量的一定数量的位来决定要“放大”多少;小数部分,然后其余的位平分为一个混合数的整数部分,而后者是一个定点数,如果设置了倒数位,则应解释为1除以该数字。这样做的好处是允许我通过使用它们的倒数(具有终止小数展开)来表示具有无限小数展开的数字,或者直接作为一个分数,潜在地作为一个近似值,这取决于我的需要。

你不能用二进制精确地表示0.1,就像你不能用传统的英国尺测量0.1英寸一样。

英国的尺子,就像二进制分数一样,都是关于一半的。你可以测量半英寸,或四分之一英寸(当然是一半),或八分之一,或十六分之一,等等。

然而,如果你想测量十分之一英寸,你就不走运了。不到八分之一英寸,但超过十六分之一英寸。如果你想更精确一点,你会发现它比3/32大一点,但比7/64小一点。我从来没见过比64分更精细的尺子,但如果你计算一下,你会发现1/10小于13/128,大于25/256,大于51/512。你可以继续越来越细,直到1024ths、2048ths、4096ths和8192,但是你会从来没有找到一个精确的标记,即使在无限细的以2为基数的尺子上,它也恰好对应于1/10或0.1。

不过,你会发现一些有趣的事情。让我们看看我列出的所有近似值,对于每一个近似值,明确地记录0.1是大是小:

分数 小数 0.1是…… 0/1
1/2 0.5 0
1/4 0.25 0
1/8 0.125 0
1/16 0.0625 更大的 1
3/32 0.09375 更大的 1
7/64 0.109375 0
13/128 0.1015625 0
25/256 0.09765625 更大的 1
51/512 0.099609375 更大的 1
103/1024 0.1005859375 0
205/2048 0.10009765625 0
409/4096 0.099853515625 更大的 1
819/8192 0.0999755859375 更大的 1

现在,如果你向下读最后一列,你会得到0001100110011。1/10的无限重复二进制分数是0.0001100110011,这不是巧合……