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

为什么输出是 not equal ?

为什么会出现这种情况?

有帮助吗?

解决方案

发生这种情况是因为在你的声明中

  if(f == 0.7)

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

  if(f == 0.7f)

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

其他提示

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

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

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

首先让我们看看浮点数的内部。我取 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),它来自指数。我们只需将上面的数字除以 2 四次:
0,100000001490116119384765625
我用的是 MS 计算器


**

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

**
我取数字0.1
它很容易,因为没有整数部分。第一个符号位 - 为 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。

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

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

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

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

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

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

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

        }

另一个近乎精确的问题与这个问题相关,因此晚了几年才得到答案。我认为以上答案并不完整。

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双3FE666666666666

正如 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包含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 是单精度,如果它们不同,则操作将提升到最高精度。

考虑一下:

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

如果 (0.7>a) 这里 a 是一个 float 变量并且 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

如果你改变数据类型 F双倍的, ,它会打印 平等的, ,这是因为浮点常量存储在 双倍的 并且非浮动 长的, ,双精度高,float精度较低,双精度值存储在 64 位二进制和浮点值存储在 32 位二进制,看到浮点数转换为二进制的方法就完全清楚了。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top