与float和float文字相比较的奇怪输出

时间:2009-12-03 11:41:52

标签: c++ c floating-point double-precision

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

为什么输出not equal

为什么会这样?

8 个答案:

答案 0 :(得分:49)

这是因为在你的陈述中

  if(f == 0.7)

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

  if(f == 0.7f)

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

答案 1 :(得分:14)

这个答案补充了现有的答案:注意0.7不能完全表示为float(或double)。如果它被准确表示,那么在转换为float然后再转换为double时不会丢失信息,并且你不会遇到这个问题。

甚至可以说有一个编译器警告文件浮点常量无法准确表示,特别是当标准对于是否在运行时以具有的模式进行舍入时非常模糊已设置为该时间或在另一个舍入模式下的编译时。

所有可以精确表示的非整数都是5作为最后一位十进制数。不幸的是,相反的情况并非如此:有些数字的最后一个十进制数字为5,无法准确表示。小整数都可以被精确地表示,并且除以2的幂除以将可以表示的数字转换为可以表示的另一个数,只要你不进入非规范化数字的领域。

答案 2 :(得分:9)

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

enter image description here
在二进制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),依此类推。我们得到的是:


enter image description here enter image description here

我们需要添加所有数字(2的幂)并添加到1(始终为1,通过标准)。是 1,60000002384185791015625
现在让我们将这个数乘以2 ^( - 4),它来自Exponent。我们只将上面的数字除以2四次:
0,100000001490116119384765625
我用过MS计算器


**

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

**
我拿数字为

它很容易,因为没有整数部分。第一个符号位 - 它是0。 我将计算指数和显着精度。逻辑乘以2整数(0.1 * 2 = 0.2),如果它大于1则减去并继续。
enter image description here
数字是.00011001100110011001100110011,标准说我们必须在得到1.之前向左移动。(某事)。您如何看待我们需要4个班次,从这个数字计算指数(127-4 = 123 )。现在显着精度是 10011001100110011001100 (并且丢失了位)。
现在整数。符号位 0 指数为123( 01111011 ),有效精度为 10011001100110011001100 ,整体为
00111101110011001100110011001100 让我们将它与前一章中的那些进行比较 的 00111101110011001100110011001101
如你所见,持续位不相等。这是因为我截断了这个数字。 CPU和编译器知道在有效精度不能保持之后就是这样,只需将最后一位设置为1。

答案 3 :(得分:1)

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

更好的做法是做类似

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

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

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

答案 4 :(得分:1)

网络上的很多答案都错误地看待浮点数之间的abosulute差异,这只适用于特殊情况,强有力的方法是查看下面的相对差异:

      // 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;

        }

答案 5 :(得分:1)

与此问题相关的另一个几乎完全相同的问题,因此答案晚了几年。我不认为上述答案是完整的。

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中的单数会转换为double并与double 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

正如亚历山大指出的那样,如果该答案仍然是IEEE 754,则

  

seeeeeeeefffffffffffffffffffffffff

双是

  

seeeeeeeeeeeffffffffffffffffffffffffffffffffffffffffffffffffffff

具有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。

,但当从双精度转换为单精度或一般表示时 作为单个IEEE754。

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

IEEE 754使用舍入模式,向上舍入,向下舍入或舍入为零。默认情况下,编译器倾向于四舍五入。如果您还记得在12345678年级学校舍入,如果我想从顶部四舍五入到第3个数字,那么应该是12300000,但是如果后面的数字是5或更大,则四舍五入到下一个数字1235000。 5是10的1/2,二进制1中的基数(十进制)是基数的1/2,因此,如果我们要舍入的位置后面的数字是1,则舍入否则不要舍入。因此,对于0.7,我们没有四舍五入,对于0.6,我们没有四舍五入。

现在很容易看到

00110011001100110011010

由于(x <0.7)转换为两倍

00110011001100110011010000000000....

大于

00110011001100110011001100110011....

因此,不必谈论使用等于问题,仍然会出现问题本身0.7是双精度0.7f是单精度,如果它们不同,则运算将提升为最高精度。

答案 6 :(得分:0)

考虑一下:

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

如果(0.7&gt; 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

答案 7 :(得分:0)

如果将 f 的数据类型更改为 double ,则会打印等于,这是因为存储在中的浮点常量> double 且不以 long 浮动,double精度较高,float的精度较低,double值存储在 64 位二进制中,float值存储在 32 位二进制,如果您看到将浮点数转换为二进制的方法,则将完全清楚。