C语言 (0.1 + 0.2)== 0.3是真还是假?

lvjbypge  于 2023-10-16  发布在  其他
关注(0)|答案(4)|浏览(148)

我对浮点数有一个基本的了解,阅读this article,它说:
0.1+ 0.2:这等于0.3,但在浮点:(0.1 + 0.2) == 0.3为false。这是因为0.1、0.2和0.3不能精确地表示为基数为2的浮点数。
好吧,根据浮点数的性质,这是真的,但我写了一个简单的程序来测试:

float a = 0.1;
float b = 0.2;

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

但输出实际上是true。以下是我的两个问题:
1.我觉得发生的情况是,因为C使用的是舍入到偶数的舍入模式,所以舍入之后,恰好为真,我的理解对吗?
1.如果我的理解是正确的,那么一定有一些指定的浮点数在这种情况下不会为真,因为舍入仍然有很小的可能失败。所以这一定是某种组合,

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

我的理解正确吗?

noj0wjuj

noj0wjuj1#

我为你的程序得到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计算速度非常快,对很多事情都很有用,但是对于某些值来说本质上是不精确的,所以你会遇到这种问题。对于float,您可以为0.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;
}
eqfvzcg8

eqfvzcg82#

在数学意义上,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为真时才为真
正如我相信你知道的错误是取决于价值和它的大小。因此,有几种情况下,错误对齐,使得浮点数似乎为相等工作,但一般情况下,这种比较通常是错误的,因为它们无法考虑错误。
要编写强大的浮点代码,您需要研究测量理论,以及如何在整个公式中传播测量误差。这也意味着你必须用“equals within the bounds of error”来替换C类型(位比较)的相等。
请注意,您无法构建一个完美地自动处理程序中错误的系统,因为这样做需要一个有限大小的精确存储方法来存储任何可能无限重复的小数位。因此,通常使用误差估计,并且通常在针对所涉及的值调谐的近似边界内比较结果。
你很快就会意识到,虽然你的程序是正确的,但你不能相信这种技术,因为这种技术通常不会返回正确的值。

6ovsh4lw

6ovsh4lw3#

通常,所有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.29999999999999889和0.30000000000000004441的比较失败了,你会不惜一切代价得到False。

hfyxw5xn

hfyxw5xn4#

这是因为浮点数在JavaScript(以及许多其他编程语言)中的表示方式。由于二进制浮点表示的精度限制,0.1 + 0.2的结果不完全是0.3。结果非常接近0.3,但不是完全匹配。

console.log(0.1 + 0.2); // Outputs: 0.30000000000000004

相关问题