浮点数:一种有漏洞的抽象【译】

译自:Floating point numbers are a leaky abstraction  Posted on 6 April 2009 by Johnhtml

  “有漏洞的抽象”(leaky abstraction)一词源自Joel Spolsky,他用这个术语表示编程时既能让人免受凌乱的细节所扰,但有时也会把事情弄砸的那些概念。完美的抽象是一个你永远不须要打开的黑盒子。有漏洞的抽象则如同一个偶尔不得不打开一下的黑盒子。编程

  做为对实数的计算机表示,浮点数就是有漏洞的抽象。一般它们表现得很不错:你甚至能够伪装把浮点类型当作数学上的实数。可是当这个有漏洞的抽象“露馅”的时候你却不能这样,虽然这种状况不是很常见。oracle

  我听到过的最多的关于机器数的局限性的解释虽看起来一本正经:“因为只有有限个浮点数,所以它们不能很好地表示实数”但这话基本没什么用!它没法解释为何在大多数应用中浮点数确实至关好地表示了实数,也没能对这个有漏洞的抽象可能会“露馅”给予任何提示。atom

  标准浮点数大体有十进制下16位的精度,最大值为10308次方的数量级,即1后面有308个0。(依据IEEE 754标准实现的典型浮点数。)spa

  十进制16位已经至关很多了。几乎没有任何可测量的量能接近那么大的精度。例如,牛顿万有引力定律中的常数仅仅已知6位有效数字。电子的电荷已知11个有效数字,虽然比牛顿的万有引力常数精度高得多,但仍远低于浮点数。那么什么时候16位有效数字不够呢?一个产生问题的地方就是减法。其余的基本运算——加法、乘法、除法都很是精确。只要你不上溢或或下溢,这些操做一般会获得直到最后一位都是正确的结果。但减法获得的却多是从精确到彻底不许确之间的任一种结果。若是两个数的n位有效数字相一致,最坏的状况下其减法可能会使n有效数字的精度所有丧失殆尽。这个问题可能出乎预料地在其余计算中间出现。从关于计算标准误差(calculating standard deviation)的这个帖中既可窥其一斑。在"浮点编程的5个技巧"(Five Tips for Floating Point Programming)帖子的导数计算部分,还有另一个例子。code

  上溢或下溢呢?你何时会须要比10308还大的数?一般你是不会须要的。但在诸如几率之类的计算中,除非你足够聪明,不然你始终须要它们。在几率计算中,计算一个天文数字般巨大的数与一个极其极其小的数相乘而获得一个普通大小的乘积是件很稀松日常的事。最终的结果适合计算机(表示)没什么问题,但计算过程当中的数可能由于上溢或下溢而不适合计算机的表示。例如大多数计算机中最大的浮点数在170的阶乘与171的阶乘之间。如此巨大的阶乘在许多应用程序中极为日常,常出如今与其余大阶乘的比值当中。参见关于如何计算那些直接计算会致使溢出的阶乘的技巧这篇帖子——"避免上溢、下溢及精度损失"(Avoiding Overflow, Underflow, and Loss of Precision)。htm

  通常状况下,你能承受对浮点算术细节的那种幸福的无知,但有时你不能。若是想了解更多,David Goldberg的论文“计算机科学家应当知道的浮点算术”(What Every Computer Scientist Should Know About Floating-Point Arithmetic)当推首选。blog

更新:见后续帖子,浮点数剖析(Anatomy of a floating point number)three

相关帖子ip

怎样计算二项式几率(How to compute binomial probabilities)
NaN, 1.#IND, 1.#INF, 及诸如此类的数(NaN, 1.#IND, 1.#INF, and all that)