C ++警告:双零除数

时间:2018-07-23 08:09:15

标签: c++ gcc floating-point divide-by-zero

案例1:

#include <iostream>

int main()
{
    double d = 15.50;
    std::cout<<(d/0.0)<<std::endl;
}

它编译时没有任何警告,并打印inf。好的,C ++可以处理零除(see it live)。

但是

案例2:

#include <iostream>

int main()
{
    double d = 15.50;
    std::cout<<(d/0)<<std::endl;
}

编译器发出以下警告(see it live):

warning: division by zero [-Wdiv-by-zero]
     std::cout<<(d/0)<<std::endl;

为什么在第二种情况下编译器会发出警告?

0 != 0.0吗?

编辑:

#include <iostream>

int main()
{
    if(0 == 0.0)
        std::cout<<"Same"<<std::endl;
    else
        std::cout<<"Not same"<<std::endl;
}

输出:

Same

7 个答案:

答案 0 :(得分:107)

浮点数除以well defined by IEEE并给出无穷大(根据分子(或NaN等于±0)的值,为正或负)。 / p>

对于整数,无法表示无穷大,并且该语言将操作定义为具有undefined behaviour,因此编译器会帮助您引导您从该路径中清除。

但是,在这种情况下,由于分子是double,除数(0)也应提升为两倍,因此没有理由在不给出警告的情况下在此处发出警告0.0,所以我认为这是一个编译器错误。

答案 1 :(得分:42)

在标准C ++中,两种情况均为undefined behaviour。任何事情都可能发生,包括格式化硬盘。您不应该期望或依赖“确定返回结果”或任何其他行为。

显然,编译器决定在一种情况下发出警告,而在另一种情况下发出警告,但这并不意味着一个代码可以,而一个代码则不能。这只是编译器生成警告的一个怪癖。

根据C ++ 17标准[expr.mul] / 4:

  

二进制/运算符产生商,二进制%运算符产生第一个表达式除以第二个表达式的余数。 如果/%的第二个操作数为零,则行为未定义。

答案 2 :(得分:12)

我最好的回答这个特定问题的猜测是,编译器会在执行intdouble转换之前发出警告。

因此,步骤如下:

  1. 解析表达式
  2. Arithmetic operator /(T, T2),其中T=doubleT2=int
  3. 检查std::is_integral<T2>::valuetrueb == 0-这会触发警告。
  4. 发出警告
  5. 执行从T2double的隐式转换
  6. 执行定义明确的划分(因为编译器决定使用IEEE 754)。

这当然是推测,并且基于编译器定义的规范。从标准的角度来看,我们正在处理可能的不确定行为。


请注意,根据GCC documentation,这是预期的行为
(顺便说一句,该标志似乎无法在GCC 8.1中明确使用)

  

-Wdiv-by-zero
      警告编译时整数除以零。这是默认值。要禁止显示警告消息,请使用-Wno-div-by-zero。浮点   不警告除以零,因为这可能是合法的方法   获得无穷大和NaN。

答案 3 :(得分:9)

在这个答案中,我不会参加UB / UB崩溃。

我只想指出00.0 是不同的,尽管0 == 0.0评估为true。 0int文字,而0.0double文字。

但是,在这种情况下,最终结果是相同的:d/0是浮点除法的,因为d是double的,因此0被隐式转换为double。

答案 4 :(得分:7)

我认为foo/0foo/0.0不相同。即,第一个(整数除法或浮点除法)的结果效果高度依赖于foo的类型,而第二个则不一样(它始终是浮点除法)。 / p>

两者是否是UB都无关紧要。引用标准:

  

允许的不确定行为包括:完全忽略具有无法预测的结果的情况,以环境特征(无论是否发出诊断消息)的翻译或程序执行过程中的行为,终止翻译或执行(发出诊断消息)。

(强调我的)

考虑“ suggest parentheses around assignment used as truth value”警告:告诉编译器您确实要使用分配结果的方法是显式的,并在分配周围加上括号。结果语句具有相同的效果,但是它告诉编译器您知道自己在做什么。关于foo/0.0的说法也可以这样:由于您是使用0.0而不是0来明确告诉编译器“这是浮点除法”,因此编译器会信任您并且不会发出警告。

答案 5 :(得分:4)

这看起来像是gcc错误,-Wno-div-by-zero clearly says的文档:

  

不要警告编译时整数被零除。 不警告将浮点除以零,因为这可能是获得无穷大和NaN的合法方法。

,在[expr.arith.conv]中所述的通常的算术转换之后,两个操作数均为 double

  

许多期望算术或枚举类型的操作数的二进制运算符都会导致转换和收益   结果类型以类似的方式。目的是产生一个通用类型,它也是结果的类型。   这种模式称为通常的算术转换,定义如下:

     

...

     

否则,如果其中一个操作数为double,则另一个应转换为double。

[expr.mul]

  

*和/的操作数应为算术或无范围的枚举类型; %的操作数应具有整数或无作用域的枚举类型。   通常对操作数进行算术转换,并确定结果的类型。

关于浮点除以零是否是未定义的行为,以及不同的实现如何处理my answer here TL; DR; 看起来gcc符合附件F ,其中浮点除以零,因此undefined在这里不起作用。对于c,答案会有所不同。

答案 6 :(得分:2)

浮点除以零的行为与整数除以零的行为不同。

IEEE floating point标准区分+ inf和-inf,而整数不能存储无穷大。整数除以零结果是不确定的行为。浮点除以零是由浮点标准定义的,结果为+ inf或-inf。