検討する
#include <iostream>
int main()
{
double a = 1.0 / 0;
double b = -1.0 / 0;
double c = 0.0 / 0;
std::cout << a << b << c; // to stop compilers from optimising out the code.
}
私はいつも、a
が+ Inf、b
が-Inf、c
がNaNになると考えてきました。しかし、厳密に言えばゼロによる浮動小数点除算の動作はndefinedであるため、上記のコードは移植性のあるC++とは見なされないという噂もあります。 (これにより、理論上、100万行とコードスタックの整合性が失われます。おっと。)
誰が正しいの?
注:実装の定義には満足していますが、猫を食べること、悪魔のくしゃみについて話しているndefined behaviour here。
整数と浮動小数点の両方によるゼロによる除算は未定義の動作です [expr.mul] p4 :
二項/演算子は商を生成し、二項%演算子は最初の式を2番目の式で除算した余りを生成します。 /または%の第2オペランドがゼロの場合、動作は未定義です。...
実装はオプションで Annex F をサポートできますが、これはゼロによる浮動小数点除算のセマンティクスを明確に定義しています。
このclangバグレポートから、 clang sanitizerは、IEC 60559ゼロによる浮動小数点除算を未定義 マクロ__ STDC_IEC_559 __が定義されており、システムヘッダーによって定義されており、少なくともclangではAnnex Fしたがって、clangの動作は未定義のままです。
C標準のAnnex F(IEC 60559/IEEE 754サポート)はゼロによる浮動小数点除算を定義していますが、clang(3.3および3.4 Debianスナップショット)はそれを未定義と見なします。これは間違っています:
Annex Fのサポートはオプションであり、サポートしていません。
#ifSTDC_IEC_559
このマクロは、当社ではなく、システムヘッダーによって定義されています。これはシステムヘッダーのバグです。 (FWIW、GCCはAnnex F、IIRCも完全にはサポートしていないため、Clang固有のバグではありません。)
そのバグレポートと他の2つのバグレポート BSan:ゼロによる浮動小数点除算は未定義ではありません および clangはISO C(IEC 60559/IEEE 754)のAnnex Fをサポートする必要があります gccは、ゼロによる浮動小数点除算に関して、Annex Fに準拠しています。
STDC_IEC_559を無条件に定義するのはCライブラリ次第ではないことに同意しますが、問題はclangに固有のものです。 GCCはAnnex Fを完全にはサポートしていませんが、少なくともその意図はデフォルトでサポートすることであり、丸めモードが変更されない場合、分割はそれで明確に定義されます。現在、IEEE 754をサポートしていない(少なくともゼロによる除算の処理のような基本機能)は、悪い動作と見なされます。
これは、gcc GCC wikiの浮動小数点演算のセマンティクス によるさらなるサポートです。これは -fno-signaling-nans が gccと一致するデフォルトであることを示します最適化オプションのドキュメント 言う:
デフォルトは-fno-signaling-nansです。
BSan clangのデフォルトでは、float-divide-by-zeroが-fsanitize = undefinedの下に含まれることに注意してくださいwhile gccはそうではありません :
ゼロによる浮動小数点除算を検出します。他の同様のオプションとは異なり、-fsanitize = float-divide-by-zeroは、-fsanitize = undefinedでは有効になりません。 。
C++標準はIEEE 754標準を強制しません。これは主にハードウェアアーキテクチャに依存しているためです。
ハードウェア/コンパイラがIEEE 754標準を正しく実装している場合、部門は予想されるINF、-INF、およびNaNを提供します。それ以外の場合は...依存します。
未定義の意味は、コンパイラの実装が決定し、ハードウェアアーキテクチャ、コード生成効率、コンパイラ開発者の怠inessなど、さまざまな変数があります。
ソース:
C++標準は、0.0による除算がundefined
であると述べています
C++標準5.6.4
... /または%の第2オペランドがゼロの場合、動作は未定義です
C++標準18.3.2.4
... static constexpr bool is_iec559;
... 56。タイプがIEC 559 standard.217に従う場合にのみtrue
... 57。すべての浮動小数点型に意味があります。
IEEE754のC++検出:
標準ライブラリには、IEEE754がサポートされているかどうかを検出するテンプレートが含まれています。
static constexpr bool is_iec559;
#include <numeric>
bool isFloatIeee754 = std::numeric_limits<float>::is_iec559();
IEEE754がサポートされていない場合はどうなりますか?
それは、通常、0による除算がハードウェア例外をトリガーし、アプリケーションを終了させることです。
引用 cppreference :
第2オペランドがゼロの場合、浮動小数点除算が行われ、タイプがIEEE浮動小数点演算をサポートしている場合(
std::numeric_limits::is_iec559
を参照)を除いて、動作は未定義です。
一方のオペランドがNaNの場合、結果はNaNです
ゼロ以外の数値を±0.0で除算すると、正しく署名された無限大が得られ、
FE_DIVBYZERO
が発生します0.0を0.0で除算するとNaNが得られ、
FE_INVALID
が発生します
ここでは浮動小数点除算について説明しているため、実際にはゼロによるdouble
除算が未定義かどうかは実装定義です。
std::numeric_limits<double>::is_iec559
がtrue
で、 "通常true
" の場合、動作は明確に定義されており、期待される結果が生成されます。
かなり安全な方法は、次のことをすることです:
static_assert(std::numeric_limits<double>::is_iec559, "Please use IEEE754, you weirdo");
...コードの近く。
0による除算は、undefined behaviorです。
C++標準(C++ 11) のセクション5.6から:
バイナリ
/
演算子は商を生成し、バイナリ%
演算子は最初の式を2番目の式で除算した剰余を生成します。/
または%
の第2オペランドがゼロの場合、動作は未定義です。整数オペランドの場合、/
演算子は、小数部分が破棄された代数商を生成します。商a/b
が結果の型で表現できる場合、(a/b)*b + a%b
はa
と等しくなります。
/
演算子の整数オペランドと浮動小数点オペランドは区別されません。標準では、オペランドに関係なく、ゼロによる除算は未定義であるとのみ規定されています。
[expr]/4にあります
式の評価中に結果が数学的に定義されていないであるか、その型の表現可能な値の範囲にない場合、動作は未定義です。 [注:C++の既存の実装のほとんどは、整数オーバーフローを無視します。ゼロ除算の処理、ゼロ除数を使用した剰余の形成、およびすべての浮動小数点例外はマシンによって異なり、通常はライブラリ関数によって調整可能です。 —注を終了]
重点鉱山
そのため、標準ではこれは未定義の動作です。さらに、これらのケースのいくつかは実際に実装によって処理され、構成可能であると言っています。したがって、実装が定義されているとは言いませんが、実装がこの動作の一部を定義していることを知らせます。
提出者の質問「だれが正しいのか」については、bothの答えが正しいと言ってもまったく問題ありません。 C標準が「未定義」として動作を記述するという事実は、基礎となるハードウェアが実際に行うことを指示するものではありません。それは単に、標準に従ってプログラムを意味のあるものにしたい場合あなたは、ハードウェアが実際にその操作を実装しているとは思わないかもしれません。しかし、IEEE標準を実装するハードウェアで実行している場合は、実際に操作が実装されており、その結果はIEEE標準で規定されているとおりです。
これは、浮動小数点環境にも依存します。
cppreferenceには詳細があります: http://en.cppreference.com/w/cpp/numeric/fenv (例はありません)。
これは、ほとんどのデスクトップ/サーバーC++ 11およびC99環境で利用できるはずです。このすべての標準化に先行するプラットフォーム固有のバリエーションもあります。
例外を有効にするとコードの実行が遅くなると予想されるので、おそらくこの理由から、デフォルトで例外を無効にすることを知っているほとんどのプラットフォームはおそらくそうです。