浮点数学是否被破坏?

0.1 + 0.2 == 0.3
-> false
0.1 + 0.2
-> 0.30000000000000004

为什么会发生?


二进制浮点数学就是这样。 在大多数编程语言中,它都基于IEEE 754标准。 JavaScript使用64位浮点表示,这与Java的double相同。 问题的症结在于数字以这种格式表示为一个整数乘以二的幂数; 不能准确地表示分母不是二的幂的有理数(如0.1 ,即1/10 )。

对于标准binary64格式的0.1 ,表示可以完全写为

  • 十进制的0.1000000000000000055511151231257827021181583404541015625 ,或
  • 在C99 hexfloat表示法中为0x1.999999999999ap-4
  • 相反,有理数为0.1 ,这是1/10 ,可以精确地写成

  • 0.1 ,十进制或
  • 0x1.99999999999999...p-4用C99 hexfloat表示法的模拟,其中...表示9的无止境序列。
  • 程序中的常量0.20.3也将近似于它们的真实值。 它恰好与0.2的最接近的double大于有理数0.2但最接近0.3 double小于有理数0.30.10.2的总和大于有理数0.3 ,因此不同意代码中的常数。

    浮点运算问题的一个相当全面的处理是每个计算机科学家应该知道的关于浮点运算的知识。 有关更易于理解的解释,请参阅floating-point-gui.de。


    硬件设计师的视角

    我相信自从我设计和构建浮点硬件之后,我应该增加一个硬件设计师的视角。 了解错误的起源可能有助于理解软件中发生的事情,最终,我希望这有助于解释浮点错误发生的原因,并且似乎随着时间的推移而积累。

    1.概述

    从工程角度来看,大多数浮点运算都会有一些错误元素,因为执行浮点计算的硬件只需要最后一个错误少于一个单元的一半。 因此,很多硬件将停止在一个精度上,这对于浮点除法中特别存在问题的单个操作来说最后一个地方的误差小于一个单元的一半是必要的。 什么构成单一操作取决于单元需要多少操作数。 大多数情况下,它是两个,但有些单位需要3个或更多的操作数。 因此,不能保证重复的操作会导致错误,因为随着时间的推移错误会累加。

    2.标准

    大多数处理器遵循IEEE-754标准,但有些使用非标准化或不同的标准。 例如,IEEE-754中存在一个非规范化模式,它允许以精度为代价来表示非常小的浮点数。 但是,下面将介绍作为典型操作模式的IEEE-754的标准化模式。

    在IEEE-754标准中,只要硬件设计者的最后一个位置小于一个单位的一半,就允许硬件设计者具有任何错误/ epsilon值,并且结果只需要小于最后一个单位的一半一个操作的地方。 这解释了为什么当重复操作时,这些错误加起来。 对于IEEE-754双精度,这是第54位,因为53位用于表示浮点数(例如5.3e5中的5.3)的数字部分(标准化),也称为尾数。 下一节将详细介绍各种浮点操作中硬件错误的原因。

    3.划分错误的原因

    浮点除法错误的主要原因是用于计算商的除法算法。 大多数计算机系统使用乘法逆运算来计算除法,主要在Z=X/YZ = X * (1/Y) 。 分频计算是迭代计算的,即每个周期计算商的某些比特直到达到所需的精度,这对于IEEE-754来说是最后一个误差小于一个单位的任何事情。 Y(1 / Y)的倒数表在慢速分割中被称为商选择表(QST),并且商选表的大小通常是基数的宽度,或者在每次迭代中计算的商数加上几个保护位。 对于IEEE-754标准,双精度(64位),它将是分频器基数的大小,加上一些保护位k,其中k>=2 。 例如,计算一次(基数为4)商的2位的分频器的典型商数选择表将是2+2= 4位(加上几个可选位)。

    3.1分数舍入误差:倒数近似

    商选择表中的倒数取决于划分方法:SRT划分的缓慢划分或Goldschmidt划分的快速划分; 每个条目根据分割算法进行修改,试图产生尽可能最低的错误。 无论如何,所有的倒数都是实际倒数的近似值,并引入了一些误差因素。 慢速除法和快速除法都是迭代计算商的,即每步计算一些商的位数,然后从被除数中减去结果,分频器重复这些步骤直到误差小于1的一半单位在最后的地方。 慢速分割方法计算每一步中商的固定数字位数,并且通常构建成本较低,并且快速分割方法计算每步的可变数字位数,并且通常构建起来更昂贵。 划分方法最重要的部分是,它们中的大多数都依赖于倒数近似的重复乘法,所以它们很容易出错。

    4.舍入其他操作中的错误:截断

    所有操作中舍入错误的另一个原因是IEEE-754允许的最终答案截断的不同模式。 有截断,圆归零,全舍入(默认),舍入和舍入。 所有方法在单个操作的最后位置引入少于一个单位的误差元素。 随着时间的推移和重复的操作,截断也会累加到最终的错误中。 这种截断误差在求幂运算中尤其成问题,其中涉及某种形式的重复乘法。

    5.重复操作

    由于进行浮点计算的硬件只需要产生一次误差小于最后一个单元的一半的结果,所以如果没有观察,错误将会在重复的操作中增长。 这就是在需要有界误差的计算中,数学家使用诸如在IEEE-754的最后一位使用圆到最接近偶数的方法的原因,因为随着时间的推移,误差更有可能互相抵消out和Interval Arithmetic结合IEEE 754舍入模式的变化来预测舍入误差并对其进行修正。 由于与其他舍入模式相比相对误差较低,舍入到最接近的偶数位(最后一位)是IEEE-754的默认舍入模式。

    请注意,默认舍入模式(最后一位舍入到最接近的偶数位)保证一次操作的最后一位的误差小于一个单位的一半。 单独使用截断,舍入和舍入可能会导致大于最后一位的一个单位的一半的误差,但是最后一位的误差不到一个单位,所以这些模式不推荐,除非它们是用于区间算术。

    6.总结

    简而言之,浮点操作错误的根本原因是硬件截断和分割情况下倒数截断的组合。 由于IEEE-754标准只需要一次操作的最后一个单位的误差小于一个单位的一半,所以重复操作中的浮点错误将加起来,除非被纠正。


    当您将.1或1/10转换为基数2(二进制)时,您会在小数点后获得一个重复模式,就像试图用10来表示1/3一样。该值不准确,因此您无法做到精确的数学与它使用正常的浮点方法。

    链接地址: http://www.djcxy.com/p/713.html

    上一篇: Is floating point math broken?

    下一篇: How to disable resizable property of textarea?