c++ float与float常值比较时的奇怪输出

4bbkushb  于 2022-12-05  发布在  其他
关注(0)|答案(8)|浏览(156)
float f = 0.7;
if( f == 0.7 )
    printf("equal");
else
    printf("not equal");

为什么输出是not equal
为什么会发生这种情况?

gev0vcfq

gev0vcfq1#

这是因为在你的陈述中

if(f == 0.7)

0.7被视为双精度浮点数。尝试使用0.7f以确保该值被视为浮点数:

if(f == 0.7f)

但正如Michael在下面的注解中所建议的,永远不要测试浮点值是否完全相等。

webghufk

webghufk2#

这个答案补充了现有的答案:注意0.7不能精确地表示为浮点数(或双精度数)。如果它被精确地表示,那么在转换为浮点数然后再转换回双精度数时就不会丢失信息,也就不会有这个问题。
甚至可以说,对于不能精确表示的文字浮点常量,应该有一个编译器警告,特别是当标准对于舍入是在运行时以当时设置的模式进行,还是在编译时以另一种舍入模式进行时,是如此模糊时。
所有可以精确表示的非整数都以5作为最后一位十进制数字。不幸的是,匡威就不成立了:有些数的最后一个十进制数字是5,因此无法精确表示。2小整数都可以精确表示,只要不进入反规格化数的领域,除以2的幂就可以将一个可以表示的数转换为另一个可以表示的数。

nbysray5

nbysray53#

首先,让我们看看浮点数的内部。我取0.1f,它是4字节长(二进制32),十六进制是

    • 三维CC CC CD**。

按标准IEEE 754把它转换成十进制我们必须这样做:

在二进制3D CC中,CC CD为

    • 0 01111011 1001100 1100 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计算器

**

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

第二次
我取数字0.1
因为没有整数部分。第一个符号位-它是0。指数和有效位精度,我现在将计算。逻辑是乘以2整数(0.1 * 2 = 0.2),如果它大于1,减去并继续。

数字是.0001100110011001100110011001100110011,标准表示在得到1之前我们必须左移。你看我们需要4次移位,从这个数字计算指数(127 - 4 =123)。现在的有效位精度是

    • 100110011001100110011001100**(并且存在丢失的位)。

现在是整数。符号位0指数为123(01111011),有效位精度为10011001100110011001100,整数为

    • 00111101110011001100110011001100**让我们将其与上一章中的内容进行比较
    • 00111101110011001100110011001100110011001101************************************************************************************************************************************************************************************************************

正如你所看到的,最后一位不相等。这是因为我截断了数字。CPU和编译器知道这是有效位之后的东西,精度不能保持,只是把最后一位设置为1。

xsuvu9jc

xsuvu9jc4#

另一个接近精确的问题与这个问题有关,因此多年来的答案。我不认为以上的答案是完整的。

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

为什么fun 3和fun 4返回一个而不返回其他的?为什么fun 5工作?
这是关于语言的。语言说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

单股3f 333333双股3fe 666666666666
正如Alexandr所指出的,如果答案仍然是IEEE 754,则单个
看啊看啊
而双是
看啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊
小数位数是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包含011001100110011001100110011作为它的分数,当它被转换回两倍时,分数是

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,但舍入到下一位1235000,如果后面的数字是5或更大,然后向上舍入。5是1/10中的2,二进制1中的基数(十进制)是基数的1/2,所以如果我们要舍入的位置后面的数字是1,那么就向上舍入,否则就不向上舍入,所以对于0.7我们不向上舍入,对于0.6我们向上舍入。
现在很容易看出

00110011001100110011010

由于(x〈0.7)而转换为双精度型

00110011001100110011010000000000....

大于

00110011001100110011001100110011....


因此,即使不讨论使用等于,问题仍然存在,0.7是双精度的,0.7f是单精度的,如果它们不同,运算将提升到最高精度。

xdyibdwo

xdyibdwo5#

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

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

假设FLT_EPSILON已定义为适合您的平台的较小浮点值。
由于舍入或初始化误差不太可能超过FLT_EPSILON的值,因此这将给予您提供所需的可靠等效性测试。

neekobn8

neekobn86#

网上的很多答案都犯了一个错误,即看浮点数之间的绝对差异,这只对特殊情况有效,可靠的方法是看相对差异,如下所示:

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

        }
pw9qyyiw

pw9qyyiw7#

想想这个

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.69999999881亿美元

1rhkuytd

1rhkuytd8#

变量和常量中保存的指针值的数据类型不同,这是数据类型精度的差异,如果将f变量的数据类型改为double,则打印equal,这是因为默认情况下,double中保存的是浮点型常量,long中保存的是非浮点型常量,double的精度比float高。如果您看到浮点数转换为二进制conversion的方法,就会完全清楚

相关问题