C++ 警告:双除以零

C++ warning: division of double by zero

案例一:

#include <iostream>

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

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

但是,

案例二:

#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

在标准 C++ 中,两种情况都是 undefined behaviour。任何事情都可能发生,包括格式化硬盘。您不应期望或依赖 "return inf. Ok" 或任何其他行为。

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

来自 C++17 标准 [expr.mul]/4:

The binary / operator yields the quotient, and the binary % operator yields the remainder from the division of the first expression by the second. If the second operand of / or % is zero the behavior is undefined.

浮点数除以零是well defined by IEEE并给出无穷大(根据分子的值(或NaN表示±0)).

对于整数,没有办法表示无穷大,并且语言定义了具有 undefined behaviour 的操作,因此编译器会帮助您引导您远离该路径。

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

我不会在这个答案中讨论 UB/不是 UB 崩溃。

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

然而,在这种情况下,最终结果是相同的:d/0 是浮点除法,因为 d 是双精度的,所以 0 被隐式转换为双精度。

我对回答这个特定问题的最佳猜测是编译器在执行int到[的转换之前发出警告 =11=].

所以,步骤是这样的:

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

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


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

-Wdiv-by-zero
Warn about compile-time integer division by zero. This is default. To inhibit the warning messages, use -Wno-div-by-zero. Floating point division by zero is not warned about, as it can be a legitimate way of obtaining infinities and NaNs.

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

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

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

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

Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).

(强调我的)

考虑“suggest parentheses around assignment used as truth value”警告:告诉编译器您真的想要使用赋值结果的方法是明确的,并添加作业周围的括号。结果语句具有相同的效果,但它告诉编译器你知道你在做什么。关于 foo/0.0 也可以这样说:因为您通过使用 0.0 而不是 0 明确告诉编译器 "This is floating point division",编译器信任您并且不会发出警告.

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

Do not warn about compile-time integer division by zero. Floating-point division by zero is not warned about, as it can be a legitimate way of obtaining infinities and NaNs.

并且在 通常的算术转换 包含在 [expr.arith.conv] 之后,两个操作数将是 double:

Many binary operators that expect operands of arithmetic or enumeration type cause conversions and yield result types in a similar way. The purpose is to yield a common type, which is also the type of the result. This pattern is called the usual arithmetic conversions, which are defined as follows:

...

Otherwise, if either operand is double, the other shall be converted to double.

[expr.mul]:

The operands of * and / shall have arithmetic or unscoped enumeration type; the operands of % shall have integral or unscoped enumeration type. The usual arithmetic conversions are performed on the operands and determine the type of the result.

关于浮点除以零是否是未定义的行为以及不同的实现如何处理它TL;DR; 看起来 gcc 符合 Annex F wrt 浮点除以零,所以 undefined 在这里不起作用。 clang 的答案会有所不同。