浮点数比较会得出不同的结果

3

看下面的两段代码,告诉我为什么答案会有很大差异。

#include<stdio.h>
int main() {
    float a = 0.9;
    if(a<0.9)
        printf("hi"); // This will be the answer
    else
        printf("bye");
    return 0;
}

如果我们将0.9更改为0.8,那么else语句会被打印出来:

#include<stdio.h>
int main() {
    float a = 0.8;
    if(a<0.8)
        printf("hi");
    else
        printf("bye");//this will be the answer
    return 0;
}

那么为什么只改变一个数字就会导致输出结果改变呢?


2
不要太激动!只是改变一个数字而已!你在任何程序中改变一个数字,输出都会发生改变。 - Shahbaz
另外,请也阅读我的回答。我一开始犯了一个错误,得到了-1分,但后来进行了编辑,现在它包含了一个常见的解决方法来解决你的问题。 - Shahbaz
7个回答

10

这个错误是由于浮点数精度问题以及将 float 类型与 double 值进行比较导致的。尝试使用浮点字面量进行比较:if(a<0.8f)

我建议您阅读相应的 维基百科文章,它详细解释了为什么会出现此错误。


@Constantinius:是的,我同意你对所问问题的解释,但为什么#include<stdio.h>也会发生同样的事情呢? int main() { float a = 0.9; if(a<0.9) printf("hi"); else printf("bye");//这将是答案 return 0; } 根据您的回答,应该打印bye,但实际上并没有...但当将0.9更改为0.8时,就可以了。 - Nishant Kumar
@Nishant:因为对于0.9来说,(不准确的)单精度值恰好小于(不准确的)双精度值,而对于0.8来说则相反。 - undur_gongor

5
字面量0.90.8的类型为double。由于两个值都无法准确表示,实际上它们将成为0.9000000000000000222...0.8000000000000000444...
当存储在float(单精度)变量a中时,它们将被转换为单精度并变得更加不准确:0.89999997...0.8000000119...
为了与字面量double值进行比较,它们被转换回double,保留更不准确的值。
从上面的数字可以看出,对于0.90.8,比较会产生不同的结果。
所有这些都假设您的平台具有IEEE754浮点数,这很可能是真的。
您可以在www.binaryconvert.com上查看数字的单/双精度表示。

1

你必须了解浮点数的工作原理。

浮点数是使用二的幂表示的,每个数字用于表示2^-x,其中X是第n个数字。

例如,0.011(二进制)将是2^-2 + 2^-3,即0.25 + 0.125 = 0.375

现在尝试表示0.9。你会遇到麻烦,因为没有二的幂来表示它。在32位和64位机器上表示的值将略小于0.9的结果,而对于0.8,结果是精确的,并且可以用二的幂表示

您可以通过打开python提示符来尝试此操作。尝试输入一些数字,最终一个数字将以...99999999结尾。


1
@wormsparty:0.8在单精度或双精度浮点数中并不能完全表示。 - undur_gongor
0.8和0.9转换为二进制数时都是无限循环的。那么是什么区别导致这两种情况的表现不同呢? - Tapasweni Pathak
问题在于0.8被放入了一个浮点数中,因此它可能以32位的二进制表示形式表示接近0.8f的数字。之后,比较是在双精度浮点数上进行的,因此为了进行比较,它将浮点数转换为64位表示形式的双精度浮点数,这不再准确地表示0.8,而是一些其他非常接近的数字。它将其与没有经过转换的双精度浮点数0.8进行比较。请查看下面的答案以获取更多详细信息。 - wormsparty

1

根据C标准:

6.3.1.5 实浮点类型

1)当float被提升为double或long double,或者double被提升为long double时,其值不变。

2)当double被降级为float,long double被降级为double或float,或者一个值被以比其语义类型(见6.3.1.8)更高的精度和范围表示,并且显式地转换为其语义类型时,如果被转换的值可以在新类型中精确表示,则它不变。如果被转换的值在可表示的值范围内但无法精确表示,则结果是实现定义方式选择的最接近的较高或较低可表示值。如果被转换的值超出了可表示的值范围,则行为未定义。

该标准可以在这里找到。


0

首先,正如其他人提到的那样,在使用 float 时,请使用像 0.8f 这样的值。

此外,应避免使用浮点数 == 比较,因为 FPU 中此操作的精度。对我而言,最好的方法始终是定义一个边界,比如说 1e-6f(或者根据您的应用程序需要的精度),而不是这样:

if (LHS == RHS)

你写:

if (LHS-RHS < MARGIN && RHS-LHS < MARGIN)

你可以编写一个函数(如果你是C++的粉丝)或宏(如果你是C的粉丝(这里来了-1s)),让它为你完成这个任务。

0
这是由于舍入误差造成的,为了查看您正在使用的值,请在if之前插入printf:
printf("a=%f\n", a);

-1
尝试这个:
不要使用 "if(a<0.9)" 进行比较,而是使用 "if(a<0.9f)"。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接