41
float f = 0.7;
if( f == 0.7 )
    printf("equal");
else
    printf("not equal");

为什么是输出not equal

为什么会这样?

4

8 回答 8

56

发生这种情况是因为在您的陈述中

  if(f == 0.7)

0.7 被视为双倍。尝试 0.7f 以确保该值被视为浮点数:

  if(f == 0.7f)

但正如迈克尔在下面的评论中建议的那样,您永远不应该测试浮点值的精确相等性。

于 2009-12-03T11:45:37.557 回答
14

这个答案是对现有答案的补充:请注意,0.7 不能完全表示为浮点数(或双精度数)。如果它被精确表示,那么在转换为浮点数然后再转换为双精度时不会丢失信息,并且您不会遇到这个问题。

甚至可以争辩说,对于无法精确表示的文字浮点常量,应该有一个编译器警告,特别是当标准对于是否在运行时以设置为的模式进行舍入如此模糊时那个时候或在编译时以另一种舍入模式。

所有可以精确表示的非整数都具有5最后一位十进制数字。不幸的是,反之亦然:有些数字5的最后一位十进制数字不能精确表示。小整数都可以精确表示,除以 2 的幂将一个可以表示的数字转换为另一个可以表示的数字,只要您不进入非规范化数字的领域。

于 2009-12-03T11:53:46.263 回答
14

首先让我们看看浮点数。我取 0.1f 它是 4 字节长(二进制 32),十六进制是
3D CC CC CD
按照标准 IEEE 754 将其转换为十进制,我们必须这样做:

在此处输入图像描述
在二进制 3D CC CC CD 中是
0 01111011 1001100 11001100 11001101
这里第一个数字是符号位。0 表示 (-1)^0 我们的数字是正数。
第二个 8 位是指数。二进制是 01111011 - 十进制是 123。但真正的指数是 123-127(总是 127)= -4,这意味着我们需要将得到的数字乘以 2^(-4)。
最后 23 个字节是有效位精度。我们将第一位乘以 1/ (2^1) (0.5),第二位乘以 1/ (2^2) (0.25),依此类推。在这里我们得到:


在此处输入图像描述 在此处输入图像描述

我们需要添加所有数字(2 的幂)并添加 1(根据标准,始终为 1)。它是
1,60000002384185791015625
现在让我们将这个数字乘以 2^ (-4),它来自指数。我们只是将上面的数字除以 2 四次:
0,100000001490116119384765625
我使用了 MS Calculator


**

现在是第二部分。从十进制转换为二进制。

**
我取数字 0.1
它很容易,因为没有整数部分。第一个符号位 - 它是 0。我现在将计算指数和有效数字精度。逻辑乘以 2 个整数(0.1*2=0.2),如果它大于 1,则减去并继续。 数字是 .00011001100110011001100110011,标准说我们必须在得到 1 之前向左移动。(某事)。你怎么看我们需要 4 班,从这个数字计算指数(127-4= 123 )。现在有效位精度为10011001100110011001100(并且有丢失的位)。 现在是整数。符号位0指数为 123 ( 01111011 ),有效位精度为10011001100110011001100
在此处输入图像描述


整体是
00111101110011001100110011001100 让我们将其与上一章中的内容进行比较
00111101110011001100110011001101
如您所见,最后一位不相等。这是因为我截断了数字。CPU 和编译器知道这是在有效位精度之后无法保持的东西,只需将最后一位设置为 1。

于 2016-07-12T09:21:44.923 回答
2

另一个近乎精确的问题与这个问题有关,因此迟到了几年的答案。我认为上述答案并不完整。

int fun1 ( void )
{
      float x=0.7;
      if(x==0.7) return(1);
      else       return(0);
}
int fun2 ( void )
{
      float x=1.1;
      if(x==1.1) return(1);
      else       return(0);
}
int fun3 ( void )
{
      float x=1.0;
      if(x==1.0) return(1);
      else       return(0);
}
int fun4 ( void )
{
      float x=0.0;
      if(x==0.0) return(1);
      else       return(0);
}
int fun5 ( void )
{
      float x=0.7;
      if(x==0.7f) return(1);
      else       return(0);
}
float fun10 ( void )
{
    return(0.7);
}
double fun11 ( void )
{
    return(0.7);
}
float fun12 ( void )
{
    return(1.0);
}
double fun13 ( void )
{
    return(1.0);
}

Disassembly of section .text:

00000000 <fun1>:
   0:   e3a00000    mov r0, #0
   4:   e12fff1e    bx  lr

00000008 <fun2>:
   8:   e3a00000    mov r0, #0
   c:   e12fff1e    bx  lr

00000010 <fun3>:
  10:   e3a00001    mov r0, #1
  14:   e12fff1e    bx  lr

00000018 <fun4>:
  18:   e3a00001    mov r0, #1
  1c:   e12fff1e    bx  lr

00000020 <fun5>:
  20:   e3a00001    mov r0, #1
  24:   e12fff1e    bx  lr

00000028 <fun10>:
  28:   e59f0000    ldr r0, [pc]    ; 30 <fun10+0x8>
  2c:   e12fff1e    bx  lr
  30:   3f333333    svccc   0x00333333

00000034 <fun11>:
  34:   e28f1004    add r1, pc, #4
  38:   e8910003    ldm r1, {r0, r1}
  3c:   e12fff1e    bx  lr
  40:   66666666    strbtvs r6, [r6], -r6, ror #12
  44:   3fe66666    svccc   0x00e66666

00000048 <fun12>:
  48:   e3a005fe    mov r0, #1065353216 ; 0x3f800000
  4c:   e12fff1e    bx  lr

00000050 <fun13>:
  50:   e3a00000    mov r0, #0
  54:   e59f1000    ldr r1, [pc]    ; 5c <fun13+0xc>
  58:   e12fff1e    bx  lr
  5c:   3ff00000    svccc   0x00f00000  ; IMB

为什么 fun3 和 fun4 返回一个而不返回其他?为什么 fun5 有效?

这是关于语言的。该语言说 0.7 是双精度,除非您使用这种语法 0.7f 然后它是单精度。所以

  float x=0.7;

双精度 0.7 转换为单精度并存储在 x 中。

  if(x==0.7) return(1);

该语言说我们必须提升到更高的精度,以便将 x 中的单精度转换为双精度并与双精度 0.7 进行比较。

00000028 <fun10>:
  28:   e59f0000    ldr r0, [pc]    ; 30 <fun10+0x8>
  2c:   e12fff1e    bx  lr
  30:   3f333333    svccc   0x00333333

00000034 <fun11>:
  34:   e28f1004    add r1, pc, #4
  38:   e8910003    ldm r1, {r0, r1}
  3c:   e12fff1e    bx  lr
  40:   66666666    strbtvs r6, [r6], -r6, ror #12
  44:   3fe66666    svccc   0x00e66666

单 3f333333 双 3fe6666666666666

正如 Alexandr 指出的那样,如果这个答案仍然是 IEEE 754,那么一个单一的就是

见eeeeeeeffffffffffffffffffffffff

双是

见啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊

有 52 位小数,而不是单曲的 23 位。

00111111001100110011... single
001111111110011001100110... double

0 01111110 01100110011... single
0 01111111110 01100110011... double

就像以 10 为底的 1/3 是 0.3333333 ......永远。我们这里有一个重复的模式 0110

01100110011001100110011 single, 23 bits
01100110011001100110011001100110.... double 52 bits.

这就是答案。

  if(x==0.7) return(1);

x 包含 01100110011001100110011 作为它的分数,当它被转换回双倍时,分数是

01100110011001100110011000000000....

这不等于

01100110011001100110011001100110...

但在这儿

  if(x==0.7f) return(1);

该提升不会发生相同的位模式相互比较。

为什么 1.0 有效?

00000048 <fun12>:
  48:   e3a005fe    mov r0, #1065353216 ; 0x3f800000
  4c:   e12fff1e    bx  lr

00000050 <fun13>:
  50:   e3a00000    mov r0, #0
  54:   e59f1000    ldr r1, [pc]    ; 5c <fun13+0xc>
  58:   e12fff1e    bx  lr
  5c:   3ff00000    svccc   0x00f00000  ; IMB

0011111110000000...
0011111111110000000...

0 01111111 0000000...
0 01111111111 0000000...

在这两种情况下,分数都是零。因此,从双精度到单精度再到双精度不会损失任何精度。它从单精度转换为双精度,并且两个值的位比较有效。

halfdan 投票和检查的最高答案是正确答案,这是混合精度的情况,您永远不应该进行相等比较。

该答案中未显示原因。0.7 失败 1.0 有效。为什么 0.7 失败没有显示。重复的问题 1.1 也失败了。


编辑

等号可以从这里的问题中取出,这是一个已经回答过的不同问题,但它是同一个问题,也有“什么……”的初始震惊。

int fun1 ( void )
{
      float x=0.7;
      if(x<0.7) return(1);
      else       return(0);
}
int fun2 ( void )
{
      float x=0.6;
      if(x<0.6) return(1);
      else       return(0);
}

Disassembly of section .text:

00000000 <fun1>:
   0:   e3a00001    mov r0, #1
   4:   e12fff1e    bx  lr

00000008 <fun2>:
   8:   e3a00000    mov r0, #0
   c:   e12fff1e    bx  lr

为什么一个显示小于,另一个显示不小于?当他们应该是平等的。

从上面我们知道 0.7 的故事。

01100110011001100110011 single, 23 bits
01100110011001100110011001100110.... double 52 bits.

01100110011001100110011000000000....

小于。

01100110011001100110011001100110...

0.6 是不同的重复模式 0011 而不是 0110。

但是当从双精度转换为单精度或通常表示为单个 IEEE 754 时。

00110011001100110011001100110011.... double 52 bits.
00110011001100110011001 is NOT the fraction for single
00110011001100110011010 IS the fraction for single

IEEE 754 使用舍入模式,向上舍入、向下舍入或舍入为零。默认情况下,编译器倾向于四舍五入。如果你记得在小学四舍五入 12345678,如果我想从顶部四舍五入到第三位,那将是 12300000,但如果后面的数字是 5 或更大,则四舍五入到下一个数字 1235000,然后四舍五入。5 是 10 的 1/2,二进制 1 中的底数(十进制)是底数的 1/2,所以如果我们要四舍五入的位置之后的数字是 1,则四舍五入,否则不要。所以对于 0.7 我们没有四舍五入,对于 0.6 我们确实四舍五入。

现在很容易看出

00110011001100110011010

由于 (x<0.7) 转换为双精度

00110011001100110011010000000000....

大于

00110011001100110011001100110011....

因此,不必谈论使用 equals 问题仍然存在 0.7 是双精度 0.7f 是单精度,如果它们不同,则操作将提升到最高精度。

于 2018-12-30T08:51:49.550 回答
1

正如其他评论者所指出的那样,您面临的问题是,测试浮点数之间的精确等效性通常是不安全的,因为初始化错误或计算中的舍入错误可能会引入微小的差异,从而导致 == 运算符返回 false。

更好的做法是做类似的事情

float f = 0.7;
if( fabs(f - 0.7) < FLT_EPSILON )
    printf("equal");
else
    printf("not equal");

假设 FLT_EPSILON 已为您的平台定义为适当小的浮点值。

由于舍入或初始化错误不太可能超过 FLT_EPSILON 的值,因此这将为您提供所需的可靠等效测试。

于 2015-09-21T00:24:25.920 回答
1

网络上的很多答案都错误地查看了浮点数之间的绝对差异,这仅对特殊情况有效,稳健的方法是查看相对差异,如下所示:

      // Floating point comparison:

        bool CheckFP32Equal(float referenceValue, float value)
        {
           const float fp32_epsilon = float(1E-7);
           float abs_diff = std::abs(referenceValue - value);

           // Both identical zero is a special case
           if( referenceValue==0.0f && value == 0.0f)
              return true;

           float rel_diff = abs_diff / std::max(std::abs(referenceValue) , std::abs(value) ); 

           if(rel_diff < fp32_epsilon)
                 return true;
           else 
                 return false;

        }
于 2016-12-26T16:57:57.917 回答
0

考虑一下:

int main()
{
    float a = 0.7;
    if(0.7 > a)
        printf("Hi\n");
    else
        printf("Hello\n");
    return 0;
}

if (0.7 > a)这里 a 是一个浮点变量并且0.7是一个双精度常数。双精度常数0.7大于浮点变量 a。因此满足 if 条件并打印'Hi'

例子:

int main()
{
    float a=0.7;
    printf("%.10f %.10f\n",0.7, a);
    return 0;
}

输出:
0.7000000000 0.6999999881

于 2016-02-05T06:43:56.573 回答
-2

保存在变量和常量中的指针值具有不同的数据类型。这是数据类型精度的差异。如果将f变量的数据类型更改为double,它将打印equal,这是因为浮点中的常量默认存储在double中,非浮点存储在long中,double 的精度高于 float。看浮点数转二进制的方法就清楚

于 2019-02-10T12:48:49.423 回答