在C语言中比较相同的浮点数值

8
当我尝试比较两个相同的float值时,下面的代码没有打印“相等的值”:

可能是重复问题:
在将浮点数与浮点数字面量进行比较时输出奇怪的结果

以下为代码:

void main()
{
    float a = 0.7;
    clrscr();
    if (a < 0.7)
        printf("value :  %f",a);
    else if (a == 0.7)
        printf("equal values");
    else
        printf("hello");
    getch();
}

感谢您的提前帮助。

2
0.7 是一个 double 值,0.7f 是一个 float - AusCBloke
2
在SO上应该有一个float vs double标签,只为那些问题 :) - hochl
7个回答

34

虽然许多人会告诉您始终使用epsilon比较浮点数(通常是一个不错的做法,但它应该是要比较值的百分比而不是固定值),但在这里实际上并不需要,因为您正在使用常量。

您在这里遇到的具体问题是:

float a = 0.7;

使用双精度常量0.7创建一个单精度数(会失去一些精度),同时:

if (a == 0.7)

将比较两个双精度数(首先提升a)。

将双精度0.7转换为浮点数a时丢失的精度在将a提升回双精度时不会恢复。

如果您将所有这些0.7值更改为0.7f(强制使用float而不是double),或者仅将a设置为double,则它将正常工作-现在我很少使用float,除非我需要保存大量数组空间。

您可以通过以下方法查看此操作:

#include <stdio.h>
int main (void){
    float f = 0.7;    // double converted to float
    double d1 = 0.7;  // double kept as double
    double d2 = f;    // float converted back to double

    printf ("double:            %.30f\n", d1);
    printf ("double from float: %.30f\n", d2);

    return 0;
}

这将输出类似以下内容(稍作修改以显示差异):

double:            0.6999999|99999999955591079014994
double from float: 0.6999999|88079071044921875000000
                            \_ different beyond here.

1
感谢您提供的答案,不建议使用epsilon。我之前写的这篇博客文章中有更多可能令人惊讶的行为示例。http://blog.frama-c.com/index.php?post/2011/11/08/Floating-point-quiz - Pascal Cuoq
Epsilon方法看起来很简单,但实际上相当复杂,因为所选择的epsilon应该是与被比较值的指数函数有关(一个常量向量,每个可能的指数对应一个解决方案)。此外,在接近指数的极值时,epsilon变得越来越无用,因为它的值越来越接近正在进行比较的值。 - Olof Forshell

4

3

您正在比较单精度近似值0.7和双精度近似值。为获得预期的输出,您应该使用:

if(a == 0.7f) // check a is exactly 0.7f

请注意,由于表示和舍入误差,从任何操作中获得精确的0.7f可能非常不可能。一般来说,您应该检查fabs(a-0.7)是否足够接近0
不要忘记,0.7f的确切值并不是真正的0.7,而是稍低:
0.7f = 0.699999988079071044921875

双精度表示的0.7的确切值是更好的近似值,但仍不完全等于0.7:

0.7d = 0.6999999999999999555910790149937383830547332763671875

为什么使用0.5f不起作用?0.5 = .1; 就是这样,它的尾数也适合于单精度浮点格式。 - SpawN

2

a 是一个 float 类型;0.7 是一个 double 类型的值。

两者之间的比较需要进行转换。编译器将会把 float 值转换为 double 值... 将一个 float 转换成 double 所得到的值与将一串文本(源代码)转换成 double 所得到的值是不同的。

但是永远不要使用 == 来比较浮点数值(floatdoublelong double)。

你可能想阅读一下 "程序员应该了解的浮点数算术知识"


0

浮点数的绝对精度不足,使得与整数相比较更加困难。请参阅this关于在C语言中比较浮点数的页面。特别地,从那里提取的一个代码片段展示了解决此问题的“变通方法”:

bool AlmostEqual2sComplement(float A, float B, int maxUlps)
{
    // Make sure maxUlps is non-negative and small enough that the
    // default NAN won't compare as equal to anything.
    assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024);
    int aInt = *(int*)&A;
    // Make aInt lexicographically ordered as a twos-complement int
    if (aInt < 0)
        aInt = 0x80000000 - aInt;
    // Make bInt lexicographically ordered as a twos-complement int
    int bInt = *(int*)&B;
    if (bInt < 0)
        bInt = 0x80000000 - bInt;
    int intDiff = abs(aInt - bInt);
    if (intDiff <= maxUlps)
        return true;
    return false;
}

一个简单而常见的解决方法是在代码中提供一个 epsilon,例如:
if (fabs(result - expectedResult) < 0.00001)

这实际上检查值之间的差异是否在一个阈值范围内。然而,有时这并不是最佳选择。请参阅链接文章以了解更多。

另一篇文章几乎是当人们在SO上询问浮点数时所链接的事实标准。


1
浮点数中不存在“绝对精度不足”的问题。它们的精度是绝对的,值是准确的。问题在于它们基于二进制算术,而我们通常使用十进制。十进制值可能有也可能没有在二进制中的精确等价物,反之亦然。0.5和10.125是精确等价物的例子。0.3和11.6则是没有等价物的例子。 - Olof Forshell

0

浮点数不能使用“==”运算符进行比较。

与其使用“==”运算符比较浮点数,您可以使用像这样的函数:

 //compares if the float f1 is equal with f2 and returns 1 if true and 0 if false
 int compare_float(float f1, float f2)
 {
  float precision = 0.00001;
  if (((f1 - precision) < f2) && 
      ((f1 + precision) > f2))
   {
    return 1;
   }
  else
   {
    return 0;
   }
 }

可以简化为:return fabs(f1 - f2) < 0.00001 - Mike Kwan
3
如果 f1 和 f2 都小于 0.00001 呢? - Olof Forshell
@OlofForshell 他可以将语句修改为 float precision = 0.00001*f1*f2;,这样如何? - enthusiasticgeek

-2
如果你需要将a0.7进行比较,则
if( fabs(a-0.7) < 0.00001 )
  //your code

这里的0.00001可以更改为更小的值(如0.00000001)或更大的值(如0.0001),这取决于您需要的精度。


2
你需要获取减法结果的绝对值,或者 epsilon 只增加一种方式。 - Mike Kwan

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