我对浮点数有基本的了解,并且正在阅读这篇文章,其中写道:
0.1 + 0.2:这等于 0.3,但在浮点中:
为 false。这是因为 0.1、0.2 和 0.3 无法用 2 进制浮点数精确表示。(0.1 + 0.2) == 0.3
嗯,根据浮点数的性质确实如此,但我写了一个简单的程序来测试:
float a = 0.1;
float b = 0.2;
if(a+b == 0.3)
{
printf("true");
} else
{
printf("false");
}
// result is true
但输出实际上是
true
。这是我的两个问题:
我认为发生的情况是,因为C使用了舍入到偶数的舍入模式,所以舍入后,它恰好是真的,我的理解正确吗?
如果我的理解是正确的,那么在这种情况下一定有一些指定的浮点数不会成立,因为舍入失败的可能性仍然很小。所以这一定是某种组合
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
作为输出,因为恰巧对于 float
,0.1 + 0.2 == 0.3
为 true。
但是,基本点仍然是float
(单精度)和
double
(双精度)使用的IEEE-754二进制浮点计算速度非常快,并且对很多事情都很有用,但对于某些值来说本质上是不精确的,所以你会遇到这样的问题。使用
float
,您会得到 true
的 0.1 + 0.2 == 0.3
,但是 false
会得到 0.1 + 0.6 == 0.7
:
#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;
}
在数学意义上,
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 类型(位比较)相等性替换为“在错误范围内相等”。
请注意,您无法构建一个完美地自动处理程序中错误的系统,因为这样做需要对任何可能无限重复的数字的小数部分采用有限大小的精确存储方法。因此,通常使用误差估计,并且通常在针对所涉及的值进行调整的近似边界内比较结果。
很快就会意识到,虽然您的程序是正确的,但您不能信任该技术,因为该技术通常不会返回正确的值。
一般来说,所有
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)
F
或 f
表示该值为 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。