int main()
{
    float a = 0.7;
    float b = 0.5;
    if (a < 0.7)
    {
       if (b < 0.5) printf("2 are right");
       else         printf("1 is right");
    }
    else printf("0 are right");
}


我希望这段代码的输出是0 are right
但令我沮丧的是,为什么是1 is right

评论

您应该阅读“每位计算机科学家都应了解的浮点算术知识”。

0.7不是浮点数,而是两倍。这可能是该行为的原因

原因之一是a是浮点数,.7是双精度数。

米奇,如果0.7 == 0.7,答案将是0,对吧

好点子。晚了。我现在应该停止!.....

#1 楼

int main()
{
    float a = 0.7, b = 0.5; // These are FLOATS
    if(a < .7)              // This is a DOUBLE
    {
      if(b < .5)            // This is a DOUBLE
        printf("2 are right");
      else
        printf("1 is right");
    }
    else
      printf("0 are right");
}


比较期间,浮点数被提升为两倍,并且由于浮点数的精度低于两倍,因此0.7的浮点数与0.7的两倍不相等。在这种情况下,当浮点数升为0.7时,其效果将不及两倍。正如克里斯蒂安所说,0.5始终是2的幂,因此该测试按预期工作:0.5 < 0.5是错误的。

所以,要么:


float更改为double,或:
.7.5更改为.7f.5f

,您将获得预期的行为。

评论


第一个if和第二个if应该以相同的方式评估吗?所以答案应该是2对或0对,但是为什么1对呢?

– sasidhar
2011年8月10日13:08

+1对于在看到问题并真正思考这里到底发生了什么的情况下,没有指出“从不比较浮点数”的唯一答案。

–克里斯蒂安·劳(Christian Rau)
2011年8月10日13:13



假定IEEE 754浮点,该行为是100%可预测的。 0.7成为最接近的双精度数(0.6999999999999999555910790149937383830547332763671875),并将其转换为浮点舍入到最接近的浮点值(0.699999988079071044921875)。

–R .. GitHub停止帮助ICE
2011年8月10日下午14:28

@sasidhar:表示为float的0.7存储为0.699999988079079071044921875,存储为双精度值的是0.699999999999999955555579079014994,所以存在0.000000011920928910669204204014994的差值0.000000011920928910669204014994,该差值在将a转换为double值时无法重新创建。

–马丁·约克
2011年8月10日16:10

@sasidhar:您应该假定不会精确存储所有浮点数,并且它们会丢失一些信息。 (因此,不应精确地比较浮点值,而应在误差范围内进行比较)。请注意,如果变量已优化,并且在程序执行期间所有值都保留在寄存器中,则可能会得到不同的结果。尝试在发布器模式下将优化器设置为最高级别,然后查看结果是否更改。

–马丁·约克
2011年8月10日16:15

#2 楼

问题是要比较的常数是double而不是float。同样,将常数更改为易于表示的值(例如5)将使它表示为0 is right。例如,

main()
{
    float a=0.25,b=0.5; 
    if(a<.25) 
       {
       if(b<.5) 
               printf("2 are right");
       else
               printf("1 is right");
       }
else
printf("0 are right");
}


输出:

0 are right

关于最有效的浮点和双精度方式的SO问题比较涵盖了该主题。

此外,cygnus上有关浮点数比较的这篇文章为我们提供了一些技巧:


IEEE float和double格式的设计目的是数字
是按字典顺序排序的,用IEEE
建筑师威廉·卡汉(William Kahan)的话,意思是“如果两个
相同格式的浮点数被排序(例如x
这意味着如果我们在内存中采用两个浮点数,则对其位进行解释
模式为整数,并进行比较,我们可以知道哪个更大,而无需进行浮点比较。在C / C ++语言中,此
比较看起来像这样:


if (*(int*)&f1 < *(int*)&f2)



这种迷人的语法意味着采用f1的地址,将其视为
整数指针,然后取消引用。所有这些指针操作看起来都很昂贵,但它们基本上都被抵消了,只是意味着“将f1
作为整数”。由于我们对f2采用相同的语法,因此整行
表示“比较f1和f2,使用它们在内存中的表示形式
解释为整数而不是浮点数”。


评论


整数比较假定sizeof(int)== sizeof(float)。处理双精度时的假设甚至更糟。

–赞·山猫
2011年8月10日16:10

它还违反了严格的别名规则。

–osgx
2011年8月11日,下午5:09

#3 楼

这是由于从float转换为double时的舍入问题

#4 楼

通常,将相等与浮点数进行比较是一项危险的工作(实际上,这是您在>的边界上进行比较时所做的事情),请记住,以十进制表示的某些分数(例如1/3)不能完全相同地表示可以说是二进制,

0.5= 0.1,在float或double中将是相同的。

0.7=0.10110011001100永远如此,不能完全用二进制表示0.7,会出现舍入错误,并且浮点数和双精度数之间可能(非常非常细微)不同。

请注意,在浮点数和双精度数之间切换会截取不同的小数位数,因此结果不一致。

#5 楼

另外,顺便说一句,您的逻辑0错误是对的。当您输出0正确时,您无需检查b。但是,对于您真正想要完成的事情,整个事情还是有些神秘。浮点数和双精度数之间的浮点比较会有分钟的变化,因此您应该根据情况比较增量“可接受”的变化。我一直通过仅执行工作的内联函数来完成此操作(使用宏对其进行了一次,但是那太乱了)。是的,这种类型的示例无处不在。阅读浮点数,并知道.7与.7f不同,并将.7分配给浮点数将把double转换为浮点数,从而改变了值的确切性质。但是,关于b的编程假设是错误的,因为您检查了a导致了问题,我必须注意:)