(注意:我将在此处附加“ 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)
注意我们在那里做的乘数运算。我们将尾数相乘并增加了指数。然后,由于尾数结尾大于2,我们通过碰撞指数对结果进行归一化。就像在对以十进制科学计数法的数字进行运算后调整指数时一样。在每种情况下,我们使用的值都有二进制形式的有限表示,因此基本乘法和加法运算输出的值也产生了带有有限表示形式的值。
现在,考虑如何将61除以10。我们将从尾数1.90625和1.25开始。用十进制表示,这是一个1.525,一个不错的短数字。但是,如果将其转换为二进制,这是什么?我们将以通常的方式进行操作-尽可能减去两个最大的幂,就像将整数小数转换为二进制一样,但是我们将使用两个的负幂:
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
正是尾数的这种舍入导致我们与浮点值相关联的精度损失。即使可以精确表示尾数(例如,仅将两个数字相加),如果在对指数进行归一化处理后,尾数需要太多位数以适合时,我们仍然会损失数值。
实际上,当我们将十进制数四舍五入到一个可控制的大小并只给出它的前几位数时,我们一直在做这种事情。因为我们用十进制表示结果,所以感觉很自然。但是,如果我们将小数点四舍五入然后将其转换为其他基数,则由于浮点数舍入,它看起来和我们获得的小数点一样难看。