(0.1 + 0.2) == 0.3 是对还是错?

is (0.1 + 0.2) == 0.3 true or false?

我对浮点数有基本的了解,正在阅读 this article 上面写着:

0.1 + 0.2: This equals 0.3, but in floating-point: (0.1 + 0.2) == 0.3 is false. This is because 0.1, 0.2 and 0.3 cannot be precisely represented in base 2 floating-point.

嗯,根据浮点数的性质,确实如此,但我写了一个简单的程序来测试:

float a = 0.1;
float b = 0.2;

if(a+b == 0.3)
{
  printf("true");
} else 
{
  printf("false");
}
// result is true

但输出实际上是true。这是我的两个问题:

  1. 我觉得是这样的,因为C用的是四舍五入的取整方式,所以四舍五入之后,正好为真,我的理解对吗?

  2. 如果我的理解是正确的,那么在这种情况下一定有一些指定的浮点数不成立,因为仍有很小的机会四舍五入失败。所以这一定是某种组合,如

    float a = ...;
    float b = ...;
    if(a+b == XXX)  // where XXX is the "intuitive" sum of a and b
    {
      printf("true");
    } else 
    {
      printf("false");   
    }
    
    //result is false now
    

我的理解正确吗?

我为你的程序得到 false,而不是你在问题中指出的 true。 (0.3 是一个 double 文字,所以我的猜测是当你在本地测试它时,你使用了一个 float 变量而不是 0.3 文字。)如果你实际使用float== 0.3f 而不是 == 0.3),你会得到 true 作为输出,因为恰好 float0.1 + 0.2 == 0.3 为真.

但是,基本点还是float(单精度)和double(双精度)使用的IEEE-754 binary floating-point ) 计算起来非常快并且对很多事情都很有用,但是对于某些值来说本质上是不精确的,所以你会遇到这样的问题。使用 float0.1 + 0.2 == 0.3 得到 true,但 0.1 + 0.6 == 0.7 得到 false

#include <stdio.h>

int main() {
    printf("%s\n", 0.1f + 0.6f == 0.7f ? "true" : "false"); // prints false
    //             ^^^^−−−^^^^−−−−^^^^−−− `float` literals
    return 0;
}

这个问题的著名 0.1 + 0.2 == 0.3 版本恰好 double:

#include <stdio.h>

int main() {
    printf("%s\n", 0.1 + 0.2 == 0.3 ? "true" : "false"); // prints false
    //             ^^^−−−^^^−−−−^^^−−− `double` literals
    return 0;
}

通常,所有 float 右值都被声明为 double(例如 0.5、11.332、8.9 等)因此,当您编写以下语句时:

float a = 0.1;
float b = 0.2;

if(a+b == 0.3)
  printf("true");
else
  printf("false");

它的计算结果为 0.1 + 0.2 = 0.3,没关系,但在右侧,0.3 并不像您预期​​的那样工作,如前所述,它被声明为 double默认情况下。

因此,编译器尝试比较值:

0.3 == 0.2999999999999999889

这显然不相等。

要解决此问题,您需要附加一个后缀来表示您尝试使用浮点值的编译器。

试试这个:

(a + b == 0.3F)

Ff表示值为float

不幸的是,您不能用 double 做同样的事情。为了证明这一点,您可以编写以下代码:

#include <iomanip>
.
.
.
cout << setprecision(20) << 0.1 << endl;
cout << setprecision(20) << 0.2 << endl;
cout << setprecision(20) << 0.3 << endl;
cout << setprecision(20) << (0.1 + 0.2) << endl;

您将了解到上述所有显示的值都将具有不同的值:

0.10000000000000000555 // 0.1
0.2000000000000000111  // 0.2
0.2999999999999999889  // 0.3 -------- NOTE
0.30000000000000004441 // 0.1 + 0.2 -- NOTE

现在,比较 NOTE 的值。他们也不平等。

因此,0.2999999999999999889 和 0.30000000000000004441 的比较失败,您无论如何都会得到 False。

在数学意义上,0.1 + 0.2 == 0.3 始终为真

在浮点意义上,0.1 + 0.2 == 0.3 只有在 error in representing 0.1 + error in representing 0.2 == error in representing 0.3

时才为真

我相信您知道错误取决于值及其大小。因此,在某些情况下,错误对齐使得浮点数似乎适用于相等,但一般情况是这种比较通常是错误的,因为它们无法解释错误。

要编写强大的浮点代码,您需要研究测量理论,以及如何在整个公式中传播测量误差。这也意味着您必须将 C 类型(位比较)等式替换为“在错误范围内的等式”。

请注意,您无法构建一个系统来完美地自动处理程序中的错误,因为这样做将需要一种有限大小的精确存储方法来存储任何可能无限重复数字的小数。因此,通常使用误差估计,并且通常在针对所涉及的值调整的近似边界内比较结果。

很快就会意识到,虽然您的程序是正确的,但您不能相信该技术,因为该技术通常不会 return 正确的值。