Bar*_*own 275 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 ...
Run Code Online (Sandbox Code Playgroud)
在二进制文件中,它们将是:
... 8 4 2 1 1/2 1/4 1/8 ...
Run Code Online (Sandbox Code Playgroud)
对这些数字也没有任何限制.位置无限增加到左侧和右侧.
Jon*_*eet 349
如果您有足够的空间,则可以精确表示十进制数- 只是不通过浮动二进制点数.如果使用浮动小数点类型(例如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.将其与另一种方式进行比较:给定任意数量的十进制数字,您可以精确地表示任何可以表示为浮动二进制点的数字.
Jam*_* M. 33
不精确的原因是数字基础的性质.在基数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.
Aak*_*shM 23
例如,数字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(假定为最低项),我们可以通过比较的主要factorisations回答这个问题b和q.如果q有任何素因素不在素因子化中b,我们将永远无法找到适合n摆脱这些因素.
因此,对于基数10,任何 具有除2或5之外的素因子的p/q地方q将不具有终止表示.
所以现在回到基数10和2,我们看到任何具有终止10表示的理性都将具有p/q恰好在其主要因子分解中q只有2s和5s 时的形式; 并且当在其素数因子分解中q只有2s 时,相同的数字将具有终止的2代表.
但其中一个案例是另一个案例的子集!每当
q只有2其主要因素分解
显然也是如此
q在其主要因子分解中只有2s和5s
换句话说,只要p/q有终止的2表示,p/q就有一个终止的10表示.反过来但是没有不持有-只要q在它的质因数分解有5,它将有一个终端10的表示,但不是一个终止2表示.这是0.1其他答案提到的例子.
所以我们有你的问题的答案 - 因为2的素数因子是10的素数因子的子集,所有2终止数字是10终止数字,但反之亦然.它不是61而不是6.1--大约是10而不是2.
作为一个结尾注释,如果一些怪人使用(比如)基数为17但我们的计算机使用了基数为5,那么你的直觉绝不会被误导 - 没有(非零,非整数)数字终止在这两种情况下!
TM.*_*TM. 15
根(数学)原因是当你处理整数时,它们是无穷无尽的.
这意味着,即使它们数量无限,我们也可以"计算"序列中的所有项目,而不会跳过任何项目.这意味着如果我们想要将项目放在610000000000000列表中的第th位置,我们可以通过公式计算出来.
然而,实数无数无限.你不能说"给我位置上的实数610000000000000"并得到答案.原因是,当您考虑浮点值时,即使在0和之间1,也存在无限数量的值.任何两个浮点数都是如此.
更多信息:
http://en.wikipedia.org/wiki/Countable_set
http://en.wikipedia.org/wiki/Uncountable_set
更新: 道歉,我似乎误解了这个问题.我的回答是为什么我们不能代表每一个真正的价值,我没有意识到浮点被自动归类为理性.
重复我在对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.
没有什么可以说我们不能在我们的表示系统中添加符号,因此我们可以使用二进制引号表示法精确表示十进制的有理数,反之亦然.
(注意:我将在此处附加“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”、“1”到“9”)。
就像十进制表示中的 1/3 (1/3 = 0.3333333... <- 有无限多个“3”)
就像二进制中的 0.1 ( 0.1 = 0.00011001100110011.... <- 有无限多个“0011”)
一切都在这个概念中。由于您的计算机只能考虑有限的数字集(十进制或二进制),因此只有某些数字可以在您的计算机中精确表示......
正如乔恩所说,3 是一个质数,它不是 10 的因数,因此 1/3 不能用以10 为底的有限数量的元素来表示。
即使使用任意精度的算术,以 2 为基数的编号位置系统也无法完全描述 6.1,尽管它可以表示 61。
对于 6.1,我们必须使用另一种表示形式(例如十进制表示形式,或允许以 2 或 10 为基数表示浮点值的 IEEE 854)
| 归档时间: |
|
| 查看次数: |
92671 次 |
| 最近记录: |