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


98

ケース1:

#include <iostream>

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

警告なしでコンパイルして出力しinfます。OK、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

9
2番目のケースでは整数としてゼロを使用し、後でdoubleを使用して計算が行われる場合でも警告を表示すると仮定します(dがdoubleの場合の動作と考えられます)。
Qubit 2018

10
本当にQoIの問題です。警告も警告の欠如も、C ++標準自体が義務付けているものではありません。GCCを使用していますか?
StoryTeller-Unslander Monica


5
@StoryTeller QoIとは?en.wikipedia.org/wiki/QoI
user202729 2018

5
あなたの最後の質問に関して、「0は0.0と同じですか?」答えはが同じであるということですが、ご存じのとおり、それはそれらが同一であることを意味しません。他の種類!「A」が65と同じではないのと同じように
リスター氏

回答:


108

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

整数の場合、無限を表す方法はなく、言語は動作を未定義の動作に定義しているため、コンパイラーはそのパスから離れるようにしています。

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


8
ただし、どちらも浮動小数点除算です。ではd/00はのタイプに変換されdます。

43
C ++は(私は別の標準を使用してコンパイラを見たことがないにも関わらず)IEEE 754を使用する必要はないことに注意してください...
Yksisarvinen

1
@hvd、良い点、その場合、これはコンパイラのバグのように見えます
Motti

14
私は、両方の場合に警告するか、どちらの場合にも警告しないかのどちらかであることに同意します(コンパイラによるゼロによる浮動小数点除算の処理の内容によって異なります)
MM

8
ゼロによる浮動小数点除算もUBであることを確認してください。これは、GCCがIEEE 754に従って実装しているだけです。ただし、必ずしもそうする必要はありません。
Martin Bonnerがモニカをサポートする

42

標準C ++では、どちらの場合も未定義の動作です。ハードドライブのフォーマットを含め、何が起こる可能性があります。「return inf。Ok」またはその他の動作を期待したり、これらに依存したりしないでください。

コンパイラは、ある場合に警告を出し、別の場合には警告を出さないようにしていますが、これは1つのコードに問題がないことを意味するわけではありません。これは、コンパイラーが生成する警告の癖です。

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

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


21
真実ではありません。浮動小数点演算では、ゼロによる除算は明確に定義されています。
Motti

9
@Motti-C ++標準のみに制限されている場合、そのような保証はありません。この質問の範囲は、正直に言うと十分に特定されていません。
StoryTeller-Unslander Monica 2018年

9
@StoryTeller私はあればあること(私はこれのために、標準文書自体を見ていないが)かなり確信しstd::numeric_limits<T>::is_iec559ているtrue、その後、ゼロ除算はTUBではありません(とほとんどのプラットフォーム上でそれはだtrueためdoublefloatポータブルあなた'LLするものの、ifまたはで明示的に確認する必要がありif constexprます。
ダニエルH

6
@DanielH-「合理的」は実際にはかなり主観的です。この質問にlanguage-lawyerのタグが付けられている場合、他の(はるかに小さい)一連の合理的な仮定があります。
StoryTeller-Unslander Monica 2018年

5
何の要件が課されていないという意味ではありません未定義、それは何の要件が課されていない意味しません:@MMそれはあなたが言ったまさにだが、何もより多くのことを覚えておいてください標準で。私は、このケースでは、実装定義しているという事実を主張するだろうis_iec559などtrueという手段の実装が標準葉が未定義という行動を文書化されています。これは、実装のドキュメントをプログラムで読み取ることができる場合の1つです。唯一のものでもありませんis_modulo。同じことが符号付き整数型にも当てはまります。

12

この特定の質問に答えるには、コンパイラがtoへの変換を実行する前に警告を発するのが私の推測です。intdouble

したがって、手順は次のようになります。

  1. 解析式
  2. 算術演算子 /(T, T2)、ここでT=doubleT2=int
  3. チェックしstd::is_integral<T2>::valueているtrueb == 0、このトリガーが警告- 。
  4. 警告を出す
  5. toを暗黙的に変換T2するdouble
  6. 明確に定義された除算を実行します(コンパイラがIEEE 754を使用することを決定したため)。

これはもちろん推測であり、コンパイラ定義の仕様に基づいています。標準的な観点から、私たちは可能な未定義の振る舞いを扱っています。


これはGCCのドキュメントによると予想される動作であることに注意してください
(このフラグはGCC 8.1では明示的に使用できないようです)

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


2
これは、C ++コンパイラの動作方法ではありません。コンパイラは、/除算であることを知るためにオーバーロード解決を実行する必要があります。左側がFooオブジェクトであり、がある場合はoperator/(Foo, int)、除算でさえない場合があります。コンパイラbuilt-in / (double, double)は、右側の暗黙的な変換を使用して選択した場合にのみ、除算を認識します。しかし、それはによる除算でint(0)はなく、による除算を行っていることを意味しdouble(0)ます。
MSalters 2018

@MSaltersこちらをご覧ください。C ++に関する私の知識は限られていますoperator /(double, int)が、リファレンスによると確かに受け入れ可能です。次に、変換は他のアクションの前に実行されると表示されますが、GCC T2は整数型であるかどうかをすばやく確認し、そうである場合はb == 0警告を発する可能性があります。それが完全に標準に準拠しているかどうかはわかりませんが、コンパイラーは警告を定義し、いつトリガーするかを自由に選択できます。
Yksisarvinen

2
ここでは、組み込み演算子について説明します。それは面白いです。実際には関数ではないため、そのアドレスを取得することはできません。したがって、operator/(double,int)実際に存在するかどうかを判断できません。コンパイラは、たとえばa/b、定数bで置き換えることによって定数を最適化することを決定する場合がありますa * (1/b)。もちろん、これはoperator/(double,double)実行時に呼び出すのではなく、より高速であることを意味しますoperator*(double,double)。しかし、今トリップするのはオプティマイザであり1/0、それがフィードしなければならない定数operator*
MSalters '23 / 07/23

一般小数点除算浮動@MSaltersは、2として、おそらく離れて例外的なケースから、乗算で置き換えることができない
user202729

2
@ user202729:GCCは整数除算に対してもそれを行います。少し沈みます。GCCは整数除算を整数乗算に置き換えます。はい、それは可能です
。GCCが

9

私はこの答えでUB / UBの失敗ではありません。

私はちょうどことを指すようにしたい00.0 異なっているにもかかわらず、0 == 0.0trueに評価します。0intリテラルで0.0あり、doubleリテラルです。

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


5
私は、これは通常の算術変換が分割することを指定することを考えると、関連してどのように表示されないdoubleことによってint意味することintに変換されdouble 、それは、標準で指定されていること0に変換0.0 (conv.fpint / 2)
MM

OPが0同じであるかどうかを知りたい@MM0.0
bolov 2018

2
質問は「ですか0 != 0.0?」と言います。OPは「同じ」かどうかを尋ねることはありません。また、質問の目的は、d/0動作が異なるかどうかに関係しているようですd/0.0
MM

2
@MM- OPは尋ねました。彼らはこれらの定数編集でまともなSOネチケットを実際に示していません。
StoryTeller-Unslander Monica

7

私はそれを主張foo/0してfoo/0.0いるではない同じ。つまり、最初の結果(整数除算または浮動小数点除算)の効果は、のタイプに大きく依存しfooますが、2番目の場合は同じではありません(常に浮動小数点除算になります)。

2つのうちいずれかがUBであるかどうかは関係ありません。標準の引用:

許容される未定義の動作は、予測できない結果で完全に状況を無視することから、環境に特徴的な文書化された方法(診断メッセージの発行の有無にかかわらず)での動作またはプログラムの実行中、翻訳または実行の終了(発行あり)までさまざまです。診断メッセージの)。

(エンファシス鉱山)

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


1
どちらも通常の算術変換を実行して共通の型に変換する必要があるため、どちらの場合も浮動小数点除算が行われます。
Shafik Yaghmour 2018

@ShafikYaghmour回答の要点を逃しました。のタイプについては触れたことがないことに注意してくださいfoo。それは意図的なものです。アサーションはfoo、浮動小数点型の場合にのみ当てはまります。
カシオルナン

私はしませんでした。コンパイラーはタイプ情報を持ち、変換を理解しています。おそらく、テキストベースの静的アナライザーがそのようなことで発見されるかもしれませんが、コンパイラーはそうすべきではありません。
Shafik Yaghmour

私のポイントは、はい、コンパイラは通常の算術変換を知っていますが、プログラマーが明示的であるときに警告を発行しないことを選択します。重要な点は、これはおそらくバグではなく、意図的な動作であることです。
カシオルナン

次に、どちらの場合も浮動小数点除算であるため、私が指摘したドキュメントは正しくありません。したがって、ドキュメントが間違っているか、診断にバグがあります。
Shafik Yaghmour

4

これはgccのバグのように見え-Wno-div-by-zero ます

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

[expr.arith.conv]でカバーされる通常の算術変換の後、両方のオペランドはdoubleになります。

算術型または列挙型のオペランドを期待する多くの2項演算子は、変換を引き起こし、同様の方法で結果型を生成します。目的は、結果のタイプでもある一般的なタイプを生成することです。このパターンは通常の算術変換と呼ばれ、次のように定義されます。

...

そうでない場合、一方のオペランドがdoubleの場合、もう一方はdoubleに変換されます。

および[expr.mul]

*および/のオペランドは、算術型またはスコープ外の列挙型でなければなりません。%のオペランドは、整数型またはスコープ外の列挙型でなければなりません。 通常の算術変換はオペランドで実行され、結果のタイプを決定します。

浮動小数点のゼロ除算が未定義の動作であるかどうか、および異なる実装がそれをどのように処理するかに関しては、ここで私の答えのようです。TL; DR; gcc は、ゼロによる浮動小数点除算の付録Fに準拠しているように見えるため、未定義はここでは役割を果たしません。答えはclangでは異なります。


2

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

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


2
これは事実ですが、どちらの場合も浮動小数点除算が実行されるため、これが質問にどのように関連するかは明確ではありません。OPのコードには整数除算はありません。
Konrad Rudolph
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.