C++ C++;警告:双精度除以零

C++ C++;警告:双精度除以零,c++,gcc,floating-point,divide-by-zero,C++,Gcc,Floating Point,Divide By Zero,案例1: #include <iostream> int main() { double d = 15.50; std::cout<<(d/0.0)<<std::endl; } #include <iostream> int main() { if(0 == 0.0) std::cout<<"Same"<<std::endl; else std::cout

案例1:

#include <iostream>

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

int main()
{
    if(0 == 0.0)
        std::cout<<"Same"<<std::endl;
    else
        std::cout<<"Not same"<<std::endl;
}
Same
编译器给出以下警告():

输出:

#include <iostream>

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

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

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

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

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

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


浮点除以零等于零,并给出无穷大(根据分子的值为正或负(或
NaN
表示±0))

对于整数,无法表示无穷大,语言定义了要执行的操作,因此编译器会帮助您避开该路径


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

我不会在这个答案中进入UB/not UB崩溃

我只想指出,
0
0.0
是不同的,尽管
0==0.0
的计算结果为true
0
int
文本,
0.0
double
文本

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

我最好的猜测是,在执行
int
double

因此,步骤如下:

  • 解析表达式
  • /(T,T2)
    ,其中
    T=double
    T2=int
  • 检查
    std::is_integral::value
    是否为
    true
    b==0
    -这会触发警告
  • 发出警告
  • 执行
    T2
    double
  • 执行定义良好的除法(因为编译器决定使用IEEE 754)
  • 这当然是推测,并且基于编译器定义的规范。从标准的角度来看,我们正在处理可能的未定义行为


    请注意,这是根据
    (顺便说一句,这个标志似乎不能在GCC 8.1中明确使用)

    -Wdiv由零开始
    关于编译时整数除以零的警告。这是默认设置。要禁用警告消息,请使用-Wno div by zero。浮点数 零除法没有被警告,因为它可以是一种合法的计算方法 获得无限和非


    浮点零除的行为不同于整数零除


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

    我认为
    foo/0
    foo/0.0
    是不同的。也就是说,第一个除法(整数除法或浮点除法)的结果在很大程度上取决于
    foo
    的类型,而第二个除法(它将始终是浮点除法)的结果并非如此

    这两者中是否有一个是UB无关紧要的。引用标准:

    允许的未定义行为范围从完全忽略情况并产生不可预测的结果,到在翻译或程序执行过程中以环境特有的记录方式(无论是否发出诊断消息),再到终止翻译或执行(发出诊断信息)

    (强调矿山)


    以“”为例警告:告诉编译器您确实想使用赋值结果的方法是显式的,并在赋值周围添加括号。结果语句具有相同的效果,但它告诉编译器您知道自己在做什么。对于
    foo/0.0
    ,也可以这样说:因为您是显式的通过使用
    0.0
    而不是
    0
    告诉编译器“这是浮点除法”,编译器信任您,不会发出警告。

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

    不要警告编译时整数除零。浮点除零没有警告,因为它可以是获取无穷大和非整数的合法方法

    在两个操作数中包含的常规算术转换之后,将为双精度:

    许多需要算术或枚举类型操作数的二进制运算符会导致转换并产生 以类似的方式创建结果类型。目的是生成一个公共类型,它也是结果的类型。 此模式称为常用算术转换,其定义如下:

    -否则,如果任一操作数为双精度,则另一个操作数应转换为双精度。

    以及:

    *和/的操作数应具有算术或非范围枚举类型;%的操作数应具有整数或非范围枚举类型。 通常对操作数执行算术转换,并确定re的类型