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

时间:2009-07-06 20:17:12

标签: math floating-point

有关于浮点表示的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 ...

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

22 个答案:

答案 0 :(得分:342)

如果有足够的空间,则可以精确表示十进制数 - 而不是通过浮动二进制点数。如果使用浮点十进制点类型(例如.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。与之相反:给定任意数量的十进制数字,可以精确表示任何可以作为浮动二进制点精确表示的数字。

答案 1 :(得分:32)

不精确的原因是数字基础的性质。在基数10中,你不能完全代表1/3。变为0.333 ...但是,在基数3中,1/3精确地用0.1表示,1/2是无限重复的小数(tresimal?)。可以有限表示的值取决于基数的唯一素因子的数量,因此基数30 [2 * 3 * 5]可以表示比基数2或基数10更多的分数。甚至更多基数210 [2 * 3 * 5 * 7]。

这是与“浮点错误”不同的问题。这种不准确性是因为几十亿的价值分布在更大的范围内。因此,如果有效位数有23位,则只能表示大约830万个不同的值。然后,8位指数提供256个用于分配这些值的选项。此方案允许最精确的小数位于0附近,因此几乎代表0.1。

答案 2 :(得分:22)

  

例如,数字61.0具有精确的二进制表示,因为任何数字的整数部分始终是精确的。但数字6.10并不准确。我所做的只是将十进制移动到一个地方,然后我突然从Exactopia转到了Inexactville。 在数学上,两个数字之间应该没有内在差异 - 它们只是数字

让我们离开基地10和2的细节片刻。让我们问一下 - 在基地b中,哪些数字有终止表示,哪些数字没有?片刻的想法告诉我们,x数字有一个终止b - 表示当且仅当存在整数nx b^n是一个整数。

因此,例如,x = 11/500有10个终止表示,因为我们可以选择n = 3然后选择x 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只有2 s 5的主要因素分析;当q在其素因数分解中只有2时,该数字将具有终止的2代表性。

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

  

q在其素数因子分析中只有2

显然也是

  

q在其素数因子分析中只有2 s和5

或换句话说,每当p/q有一个终止的2个表示时,p/q都有一个终止的10个表示。然而,反过来保持 - 只要q在其素数因子分解中具有5,它将具有终止10表示,但终止2-表示。这是其他答案中提到的0.1示例。

所以我们有你的问题的答案 - 因为2的素数因子是10的素数因子的子集,所有2终止数字是10个终止数字,但反之亦然。它不是61而不是6.1--大约是10而不是2。

作为一个结束语,如果一些怪人使用(比如说)基数为17,但是我们的计算机使用了基数为5,那么你的直觉绝不会被误导 - 会有没有(非-zero,非整数)数字在两种情况下终止!

答案 3 :(得分:15)

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

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

然而,实数无数无限。你不能说“给我位置610000000000000的真实数字”并得到回答。原因是,即使在01之间,当您考虑浮点值时,也会有无数个值。任何两个浮点数都是如此。

更多信息:

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

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

<强>更新 我道歉,我似乎误解了这个问题。我的回答是为什么我们不能代表每个真实的值,我没有意识到浮点被自动归类为理性。

答案 4 :(得分:9)

重复我在对Skeet先生的评论中所说的内容:我们可以代表1 / 3,1 / 9,1 / 27或十进制表示法中的任何理性。我们通过添加额外的符号来实现。例如,在数字的十进制扩展中重复的数字上的一行。我们需要将十进制数表示为二进制数序列 1)一系列二进制数, 2)一个小数点, 3)一些其他符号表示序列的重复部分。

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

没有什么可以说我们不能在我们的表示系统中添加符号,因此我们可以使用二进制引号表示法精确表示十进制的有理数,反之亦然。

答案 5 :(得分:6)

BCD - Binary-coded Decimal - 表示是准确的。它们的空间效率不是很高,但在这种情况下,您需要做出准确的权衡。

答案 6 :(得分:4)

如果你使用浮点数就足够大(因为它可以做指数),那么你也会在小数点前面得到不精确的结果。所以我不认为你的问题是完全有效的,因为前提是错误的;不是这样的情况,换算10总会产生更高的精度,因为在某些时候,浮点数必须使用指数来表示数字的大小,并且也会以这种方式失去一些精度。

答案 7 :(得分:4)

同样的原因,你不能完全代表基数10的1/3,你需要说0.33333(3)。在二进制中,它是相同类型的问题,但只是针对不同的数字集发生。

答案 8 :(得分:4)

这是一个很好的问题。

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

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

但是一些(大多数)需要无限数量的元素(二进制位置为“0”或“1”,小数点为“0”,“1”到“9”)表示)。

像十进制表示中的1/3(1/3 = 0.3333333 ...&lt; - 带有无限数量的“3”)

与二进制中的0.1相似(0.1 = 0.00011001100110011 ....&lt; - 具有无限数量的“0011”)

一切都在于这个概念。由于您的计算机只能考虑有限数字组(十进制或二进制),因此只能在您的计算机中准确表示某些数字......

正如Jon所说的那样,3是一个不是因子10的素数,所以1/3不能用基数10中有限的元素数来表示。

即使使用任意精度的算术,基数2中的编号位置系统也不能完全描述6.1,尽管它可以代表61。

对于6.1,我们必须使用另一种表示(如十进制表示,或IEEE 854,允许基数2或基数10表示浮点值)

答案 9 :(得分:4)

(注意:我会在这里附加'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.11101b * 2 ^ 101b)*(1.01b * 2 ^ 11b)=(10.0110001b * 2 ^ 1000b)=(1.00110001b * 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.11101b / 1.01b = 1.10000110011 ... b我们的机器只有很多位来保存尾数,所以它们只是围绕分数并假设超过某一点的零。将61除以10时看到的错误是:

之间的差异

1.100001100110011001100110011001100110011 ... b * 2 ^ 10b
而且,说:
1.100001100110011001100110b * 2 ^ 10b

这是尾数的舍入导致我们与浮点值相关联的精度损失。即使尾数可以准确表达(例如,当只是添加两个数字时),如果尾数需要太多的数字以适合归一化指数后,我们仍然会得到数字丢失。

当我们将十进制数字舍入到可管理的大小并且只给出它的前几位数字时,我们实际上就是这样做的。因为我们用十进制表达结果感觉很自然。但是如果我们舍入小数然后将其转换为不同的基数,它看起来就像我们得到的小数点一样丑陋,因为浮点舍入。

答案 10 :(得分:3)

我很惊讶没有人说过这个:使用continued fractions。任何有理数都可以用二进制方式有限地表示。

一些例子:

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

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

除了以完美的准确度存储您的数字之外,连续分数还具有一些其他好处,例如最佳有理逼近。如果您决定提前终止连续分数中的数字序列,则剩余数字(重新组合成分数时)将为您提供最佳分数。这就是如何找到pi的近似值:

Pi的持续分数:

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

将序列终止为1,这给出了分数:

355/113

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

答案 11 :(得分:2)

在等式中

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))

答案 12 :(得分:1)

并行可以由分数和整数组成。一些分数,例如1/7,不能用十进制形式表示,没有大量的小数。因为浮点是基于二进制的,所以特殊情况会发生变化,但同样存在精确度问题。

答案 13 :(得分:1)

有一个阈值,因为数字的含义已从整数变为非整数。要表示61,你有6 * 10 ^ 1 + 1 * 10 ^ 0; 10 ^ 1和10 ^ 0都是整数。 6.1是6 * 10 ^ 0 + 1 * 10 ^ -1,但10 ^ -1是1/10,绝对不是整数。这就是你最终进入Inexactville的方式。

答案 14 :(得分:1)

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


float a = 60;
float b = 0.1;
float c = a + b * 10;

c的价值是多少?它不完全是61,因为b不是真的.1因为.1没有精确的二进制表示。

答案 15 :(得分:0)

一个简单的答案:计算机没有存储小数的无限内存(在将小数表示为科学计数形式之后)。根据IEEE 754双精度浮点数标准,我们只能存储53位小数。 有关更多信息:http://mathcenter.oxford.emory.edu/site/cs170/ieee754/

答案 16 :(得分:0)

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

浮点和整数表示为所表示的数字提供网格或格子。算术完成后,结果会从网格中掉落,并且必须通过舍入返回到网格中。例如二进制网格上的1/10。

如果我们使用二进制编码的十进制表示作为一位绅士建议,我们能否在数字上保留数字?

答案 17 :(得分:0)

其他 20 个回答已经总结过的我就不重复了,我就简单回答一下:

内容中的答案:

<块引用>

为什么不能以两个数为基数准确地表示某些比率?

出于同样的原因,小数不足以表示某些比率,即分母包含除 2 或 5 以外的质因数的不可约分数,至少在其十进制展开式的尾数中始终具有不定字符串。

<块引用>

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

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

由于模型是反映现实的主观事物,因此我们不需要严格地将二进制字符串解释为 2 的负幂和正幂之和。相反,人们可能会观察到我们可以创建一组任意的符号,这些符号使用以 2 或任何其他基数为基数来精确表示任何数字或比率。试想一下,我们可以用一个词甚至一个符号来指代所有无穷大,而无需“显示无穷大”本身。

例如,我正在为混合数设计一种二进制编码,这样我可以获得比 IEEE 754 浮点数更高的精度和准确度。在写这篇文章的时候,想法是有一个符号位、一个倒数位、一定数量的标量位来确定“放大”小数部分的程度,然后剩下的位在混合数的整数部分,后者是定点数,如果设置了倒数位,则应解释为除以该数。这样做的好处是允许我通过使用具有终止小数展开式的倒数来表示具有无限小数展开式的数字,或者根据我的需要,直接作为分数,可能作为近似值。

答案 18 :(得分:0)

上面得分高的答案将其钉死了。

首先你在你的问题中混合了基地2和基地10,然后当你在右边放一个不可分割到基地的数字时你会遇到问题。像十进制中的1/3,因为3不会进入二进制的10或1/5的幂,而不是2的幂。

另一个评论虽然从不使用等于浮点数,期间。即使它是一个精确的表示,在某些浮点系统中也有一些数字可以用多种方式准确表示(IEEE对此不好,这是一个可怕的浮点规范,所以期待头痛)。没有什么不同,这里1/3与计算器0.3333333上的数字不相等,无论小数点右边有多少3。它是或可以足够接近但不相等。所以你会期望像2 * 1/3这样的东西不等于2/3,这取决于四舍五入。永远不要使用等于浮点数。

答案 19 :(得分:0)

你知道整数是对的吗?每个位代表2 ^ n


2 ^ 4 = 16
2 ^ 3 = 8
2 ^ 2 = 4
2 ^ 1 = 2
2 ^ 0 = 1

对于浮点(具有一些区别)它是相同的但是位代表2 ^ -n 2 ^ -1 = 1/2 = 0.5
2 ^ -2 = 1 /(2 * 2)= 0.25
2 ^ -3 = 0.125
2 ^ -4 = 0.0625

浮点二进制表示:

符号指数分数(我认为隐形1附加到分数)
B11 B10 B9 B8 B7 B6 B5 B4 B3 B2 B1 B0

答案 20 :(得分:0)

有无穷多的有理数,以及用于表示它们的有限位数。请参阅http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems

答案 21 :(得分:0)

数字61.0确实具有精确的浮点运算 - 但对于所有整数而言并非如此。如果你写了一个循环,在双精度浮点数和64位整数中都加了一个,最终你会达到一个64位整数完全代表一个数字的点,但浮点数不是 - 因为没有足够的重要位。

在小数点右侧达到近似点要容易得多。如果你开始用二进制浮点写出所有数字,那就更有意义了。

另一种思考方式是,当你注意到61.0在基数10中是完全可表示的,并且小数点的移动并没有改变那个,你就是乘以十的幂(10 ^ 1,10 ^ -1)。在浮点时,乘以2的幂不会影响数字的精度。尝试取61.0并将其重复除以3,以说明完全精确的数字如何失去其精确表示。