web-dev-qa-db-ja.com

C ++の警告:doubleの0による除算

ケース1:

#include <iostream>

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

警告なしにコンパイルされ、infが出力されます。さて、C++はゼロによる除算を扱うことができます( それを見てください ).

しかし、

ケース2:

#include <iostream>

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

コンパイラは次の警告を表示します( 実際に動作している )。

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

2番目のケースでコンパイラが警告を出すのはなぜですか?

0 != 0.0はありますか?

編集:

#include <iostream>

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

出力:

Same
97
Jayesh

ゼロによる浮動小数点除算は、 IEEEによって明確に定義されています そして無限大を与えます(分子の値に従って正または負のいずれか)。 (または±0の場合はNaN ).

整数の場合、無限大を表現する方法はなく、言語は 未定義の動作 を持つように操作を定義しているので、コンパイラはそのパスからユーザをクリアしようとします。

ただし、この場合、分子はdoubleなので、除数(0)も倍精度に昇格する必要があり、ここで警告を出す理由はありませんが0.0に対して警告を出さないので、これはコンパイラのバグであると思います。

107
Motti

標準C++では、どちらの場合も 未定義の動作 です。ハードドライブのフォーマットなど、何でも起こります。あなたは "return inf。Ok"や他の行動を期待したり頼りにしてはいけません。

コンパイラは、あるケースではなく他のケースで警告を出すことにしているようですが、これは1つのコードが問題ないことを意味するわけではありません。これは、コンパイラが生成する警告の奇妙な表現です。

C++ 17標準から[expr.mul]/4:

2進/演算子は商を生成し、2進%演算子は最初の式を2番目の式で除算した余りを生成します。 /または%の2番目のオペランドがゼロの場合、動作は未定義です

42
M.M

この特別な質問に答えるための私の最も良い推測は、コンパイラが警告を発するということです 前にintからdoubleへの変換の実行。

そのため、手順は次のようになります。

  1. 式を解析する
  2. 算術演算子/(T, T2)、ここでT=doubleT2=int
  3. std::is_integral<T2>::valuetrueおよびb == 0であることを確認してください - これは警告を引き起こします。
  4. 警告を発する
  5. T2からdoubleへの暗黙の変換を実行する
  6. 明確に定義された除算を実行します(コンパイラがIEEE 754を使用することを決定したため)。

これは憶測であり、コンパイラ定義の仕様に基づいています。標準的な観点からは、可能な未定義の動作を扱っています。


これは GCCのドキュメント に従って予想される動作です。
(ところで、このフラグはGCC 8.1では明示的に使用できないようです)

-Wdiv-by-zero
コンパイル時のゼロによる整数除算について警告します。これはデフォルトです。警告メッセージを表示しないようにするには、-Wno-div-by-zeroを使用します。ゼロによる浮動小数点除算は、無限大およびNaNを取得するための正当な方法である可能性があるため、警告されません。

12
Yksisarvinen

私はこの答えでUB/UBではない大惨事に陥らないでしょう。

0がtrueと評価されているにもかかわらず、0.00 == 0.0が異なることを指摘したいだけです。 0intリテラル、0.0doubleリテラルです。

ただし、この場合、最終結果は同じです。d/0は浮動小数点除算です。これは、dがdoubleであり、したがって0が暗黙的にdoubleに変換されるためです。

9
bolov

私はfoo/0foo/0.0は同じ{notであると主張します。つまり、最初の結果(整数除算または浮動小数点除算)の結果はfooの型に大きく依存しますが、2番目の結果も同じではありません(常に浮動小数点除算になります)。

どちらかがUBかどうかは関係ありません。標準を引用する:

許されている未定義の振る舞いは予測不可能な結果で状況を完全に無視することから環境に特有の文書化された方法で翻訳またはプログラム実行中に振る舞うまで(診断メッセージの発行ありまたはなし) _翻訳の終了までまたは(診断メッセージの発行を伴う)実行。

(私の強調)

真偽値として使用される代入を括弧で囲む 」という警告を考慮してください。あなたが本当にを使いたいことをコンパイラに伝える方法代入の結果は、明示的になり、代入の周りに括弧を追加することによって行われます。結果として得られるステートメントは同じ効果を持ちますが、コンパイラにあなたが何をしているのかを知らせます。 foo/0.0についても同じことが言えます。0.0の代わりに0を使用してコンパイラに "これは浮動小数点除算である"と明示的に伝えているので、コンパイラはあなたを信頼し、警告を発行しません。

7
Cássio Renan

これはgccのバグのように見えます、-Wno-div-by-zeroのドキュメントには明らかに が書かれています。

コンパイル時のゼロによる整数除算について警告しないでください。 浮動小数点数の0による除算は、無限大およびNaNを取得するための正当な方法である可能性があるため、警告されません

通常の算術変換後[expr.arith.conv] で囲まれている場合、両方のオペランドは次のようになります。double

算術型または列挙型のオペランドを期待する多くの二項演算子でも、変換が行われ、同様に結果型が返されます。目的は、結果の型でもある共通の型を生成することです。このパターンは通常の算術変換と呼ばれ、次のように定義されています。

...

- それ以外の場合、どちらかのオペランドがdoubleの場合、もう一方はdoubleに変換されます。

and [expr.mul]

*および/のオペランドは、算術またはスコープなしの列挙型になります。 %のオペランドは、整数型またはスコープなしの列挙型を持つものとします。 通常の算術変換がオペランドに対して実行され、結果のタイプが決まります。

浮動小数点をゼロで割ることが未定義の振る舞いであるかどうか、そして異なる実装がそれをどう扱うかということに関しては 私の答えはここ です。 TL; DR;gccは、Annex F wrtを浮動小数点のゼロ除算に準拠しているように見えます。このように未定義はここでは役割を果たしません。答えはclangによって異なります。

4
Shafik Yaghmour

ゼロによる浮動小数点除算は、ゼロによる整数除算とは異なる動作をします。

IEEE浮動小数点 標準は+ infと-infを区別しますが、整数は無限大を格納できません。ゼロによる整数除算の結果は未定義の動作です。ゼロによる浮動小数点除算は、浮動小数点標準によって定義され、+ infまたは-infになります。

2
Rizwan