【问题标题】:Comparing Same Float Values In C [duplicate]在C中比较相同的浮点值[重复]
【发布时间】:2012-03-24 09:41:05
【问题描述】:

可能重复:
strange output in comparison of float with float literal

当我尝试比较 2 个相同的 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();
}

提前致谢。

【问题讨论】:

标签: c++ c floating-point floating-accuracy


【解决方案1】:

虽然许多人会告诉您始终将浮点数与 epsilon 进行比较(这通常是一个好主意,尽管它应该是被比较值的百分比而不是固定值),但这里实际上没有必要,因为您正在使用常量。

您的具体问题是:

float a = 0.7;

使用 double 常量 0.7 创建一个 精度数(损失一些精度),同时:

if (a == 0.7)

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

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

如果您将所有这些 0.7 值更改为 0.7f(强制浮动而不是双精度),或者如果您只是将 a 设为双精度,它会正常工作 - 我现在很少使用 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.

【讨论】:

  • 感谢您撰写不建议使用 epsilon 的答案。我前段时间写的这篇博文有更多可能令人惊讶的行为示例。 blog.frama-c.com/index.php?post/2011/11/08/Floating-point-quiz
  • epsilon 方法看起来很简单,但实际上非常复杂,因为选择的 epsilon 应该是被比较值的指数的函数(一个常数向量,每个可能的指数是一个解决方案) .此外,当您接近指数 epsilon 的极值时,随着它的值接近被比较的值,它的用处会越来越小。
【解决方案2】:

浮点数并非您认为的那样:这里有两个提供更多信息的来源:What Every Computer Scientist Should Know About Floating-Point ArithmeticThe Floating-Point Guide

简短的回答是,由于浮点数的表示方式,您无法进行基本的比较或算术运算并期望它能够工作。

【讨论】:

    【解决方案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;就是这样,它的尾数也适合单精度浮点格式。
    【解决方案4】:

    a 是一个float0.7double 类型的值。

    两者之间的比较需要转换。编译器会将float 值转换为double 值......并且将float 转换为double 产生的值与编译器转换文本字符串产生的值不同(源代码)加倍。

    但永远不要将浮点值(floatdoublelong double)与 == 进行比较。

    您可能想阅读"What Every Programmer Should Know About Floating-Point Arithmetic"

    【讨论】:

      【解决方案5】:

      浮点数不得与“==”运算符进行比较。

      您可以使用这样的函数,而不是使用“==”运算符比较浮点数:

       //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) &lt; 0.00001
      • 呃……如果 f1 和 f2 小于 0.00001 怎么办?
      • @OlofForshell 怎么样,他可以将语句修改为float precision = 0.00001*f1*f2;
      【解决方案6】:

      浮点数缺乏绝对精度使得进行琐碎的比较比整数更难。请参阅this 页面上比较 C 中的浮点数。特别是,从那里提取的一个代码 sn-p 展示了针对此问题的“解决方法”:

      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)
      

      这实质上检查了值之间的差异是否在阈值内。请参阅链接文章,了解为什么这并不总是最佳的:)

      Another article 几乎是人们询问 SO 上的浮动时所关联的事实标准。

      【讨论】:

      • 不存在“浮点数缺乏绝对精度”之类的东西。它们的精度是绝对的,值是准确的。问题是它们基于以 2 为底的算术,我们通常使用以 10 为底的算术。以 10 为底的值可能或可能不具有以 2 为底的精确等价物,反之亦然。 0.5 和 10.125 是完全等效的示例。 0.3 和 11.6 没有等效项的示例。
      【解决方案7】:

      如果你需要比较 a0.7

      if( fabs(a-0.7) < 0.00001 )
        //your code
      

      这里 0.00001 可以更改为更少(如 0.00000001)或更多(如 0.0001)> 这取决于您需要的精度。

      【讨论】:

      • 你需要得到减法结果的绝对值,否则epsilon只会增长一种方式。
      猜你喜欢
      • 2015-09-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-02
      • 2011-10-24
      • 2023-04-05
      相关资源
      最近更新 更多