0.1 + 0.2 - 0.3 在 IEEE 754 中给出非零,但在传统浮点中给出零,这是真的吗?

问题描述 投票:0回答:2

如果我使用 Ruby,并显示

0.1 + 0.2 - 0.3
:

3.1.2 :001  > 0.1 + 0.2 - 0.3
 => 5.551115123125783e-17 

数字可以这样显示:

3.1.2 :001 > "%0122.120f" % [0.1 + 0.2 - 0.3]
 => "0.000000000000000055511151231257827021181583404541015625000000000000000000000000000000000000000000000000000000000000000000"

问题是:它使用 IEEE 754 并给出非零:

3.1.2 :002 > 0.1 + 0.2 - 0.3 == 0
 => false 

但是,如果它是严格的传统浮点数,我们存储有效数和指数(或者 Donald Knuth 所说的分数和指数),那么它会严格给出 0 吗?

floating-point ieee-754
2个回答
4
投票

首先,除非您指定确切的传统,否则不存在所谓的“传统浮点”。在 IEEE754 之前,硬件或流行库中有大约 60 种不同的浮点实现,具有固定格式;您可以看到一个很好的调查,例如,here - 我不确定某些实现是否会丢失。那么,哪一种是“传统的”呢? ;) 正如 @ChrisDodd 指出的,IEEE754 现在可以称为“传统”,因为它自 20 世纪 80 年代初以来就被广泛使用,并且自 1990 年代以来几乎全部使用(除了强制遗留)。嗯,新一代的程序员和用户已经诞生、成长、在学校、大学学习并开始工作,他们除了 IEEE754 之外没有使用任何东西。 (免责声明:我没有考虑机器学习中使用的特殊格式,例如 16 位格式,即 32 位 IEEE754,尾数宽度被缩减;以及其他非通用计算应用。)

但是,我会在更广泛的背景下考虑你的问题。具体例子0.1+0.2-0.3不是主要的。我们可以考虑 0.3+1.1-1.4、0.01+0.06-0.07 或数百万种其他情况。我花了不到一分钟就找到了这些案例。另一方面,使用 IEEE754 double,0.01+0.02-0.03 为零。为什么?因为星星就是这样安定下来的。如果您使用另一个binary浮点实现,它的行为可能相反:0.1+0.2-0.3==0,但0.01+0.02-0.03!=0。谁知道?

最主要的是,正如其他评论者已经说过的那样,二进制浮点问题基本上是不可避免的。任何此类实现都会遇到这样的情况:有限的尾数长度和舍入会破坏在小数情况下精确的等式。所以,如果你的“传统”是使用纸张、算盘(任何类型,如宽版本)、算术计进行前计算机时代的计算 - 你是对的,这些实现没有这种错误。如果还有其他内容 - 请具体说明您的意思。

注意,IEEE754-2008 定义了十进制实现。实际上没有人使用它们。除了 IBM zSeries 和 pSeries 之外,我还没有在硬件中看到过实现。问题出在域中。通常,小数分数用于财务会计和税务。这里可以使用十进制浮点数,但实际上这个域几乎到处都需要定点计算。使用浮点会遇到无声精度损失的风险,这是不可接受的。嗯,IBM 有自己独特的客户群,但几乎所有其他客户群都并非如此。

所以:首先确定您的域名。如果这是会计/税务,十进制定点是你最好的朋友,你永远不会遇到像 0.1+0.2!=0.3 这样的问题。否则,很可能您使用 IEEE754 二进制文件是安全的,即使每次都发生同样的问题,对您来说也不重要。


1
投票

IEEE 754 传统的浮点数,事实上它使用基数2,这意味着它不能精确地表示0.3,从而导致了这个问题。

如果您使用十进制浮点数,例如 IEEE 754-2008,那么它可以精确地表示 0.3,并且在这种情况下您不会出现舍入错误。然而,在其他情况下您仍然会遇到舍入错误。

© www.soinside.com 2019 - 2024. All rights reserved.