web-dev-qa-db-ja.com

ゼロに近い浮動小数点値はゼロ除算エラーの原因になりますか?

皆さんは、フロートを直接比較するのではなく、許容値を使用することになっていることを知っています。

float a,b;
float epsilon = 1e-6f;
bool equal = (fabs(a-b) < epsilon);

除算で使用する前に値をゼロと比較するのに同じことが当てはまるのかと思っていました。

float a, b;
if (a != 0.0f) b = 1/a; // oops?

この場合、イプシロンと比較する必要もありますか?

59
neuviemeporte

ゼロによる浮動小数点除算はエラーではありません。浮動小数点例外をサポートする実装で浮動小数点例外を発生させ(積極的にチェックしない限り何もしません)、明確な結果が得られます:正または負の無限大(分子がゼロでない場合)、またはNAN(分子がゼロの場合)。

また、分母がゼロではないがゼロに非常に近い(非正規など)結果として無限大(およびオーバーフロー例外)を取得することも可能ですが、これもエラーではありません。それはまさに浮動小数点の仕組みです。

編集:エリックがコメントで指摘したように、この回答は、浮動小数点の動作を詳細に記述し、IEEE標準に合わせて浮動するC標準のオプション部分であるAnnex Fの要件を前提としていることに注意してくださいポイント。 IEEE算術演算がない場合、Cはゼロによる浮動小数点除算を定義しません(実際、すべての浮動小数点演算の結果は実装定義であり、完全なナンセンスとして定義され、依然としてC標準に準拠しています)。 IEEE浮動小数点を尊重しない風変わりなC実装を扱っている場合は、この質問に答えるために使用している実装のドキュメントを参照する必要があります。

67
R..

はい、小さな数で割ると、状況によっては、トラップを含めてゼロで割った場合と同じ結果になることがあります。

一部のC実装(およびその他のコンピューティング環境)は、特に高性能のオプションが使用される場合、フラッシュアンダーフローモードで実行される場合があります。このモードでは、非正規数による除算は、ゼロによる除算と同じ結果になる可能性があります。フラッシュアンダーフローモードは、ベクトル(SIMD)命令が使用される場合も珍しくありません。

非正規数は、仮数の暗黙ビットが1ではなく0であるほど小さい浮動小数点形式の最小指数を持つものです。IEEE754、単精度の場合、これは2未満の大きさの非ゼロ数です。-126。倍精度の場合、大きさが2未満の非ゼロの数値です-1022

(IEEE 754に準拠した)非正規数を正しく処理するには、一部のプロセッサーで追加の計算時間が必要です。必要のないときにこの遅延を回避するために、プロセッサには非正規オペランドをゼロに変換するモードがある場合があります。通常の結果が有限であっても、数値を非正規オペランドで除算すると、ゼロで除算した場合と同じ結果になります。

他の回答で述べたように、C規格のAnnex Fを採用するC実装では、ゼロで割ることはエラーではありません。すべての実装がそうするわけではありません。実装されていない場合、浮動小数点トラップ、特にゼロ除算例外のトラップが有効になっているかどうかは、環境に関する追加仕様なしでは確認できません。

状況によっては、アプリケーションの他のコードが浮動小数点環境を変更するのを防ぐ必要もあります。

24

投稿のタイトルにある質問に答えるために、非常に小さな数で除算してもゼロによる除算は行われませんが、結果が無限になる可能性があります。

double x = 1E-300;
cout << x << endl;
double y = 1E300;
cout << y << endl;
double z = y / x;
cout << z << endl;
cout << (z == std::numeric_limits<double>::infinity()) << endl;

これ produces 次の出力:

1e-300
1e+300
inf
1
9
dasblinkenlight

正確に0.fによる除算のみがゼロ除算例外を発生させます。

ただし、非常に小さい数で除算するとオーバーフロー例外が発生する可能性があります。結果が非常に大きいため、フロートで表すことができなくなります。除算は無限大を返します。

無限大の浮動小数点表現は計算に使用できるため、実装の残りの部分で処理できるかどうかを確認する必要はありません。

7
Jens Agby

この場合、イプシロンと比較する必要もありますか?

0.0fIEEE float で正確に表されるため、ゼロ除算エラーを受け取ることはありません。

ただし、アプリケーションに完全に依存しますが、まだある程度の許容範囲を使用したい場合があります。 「ゼロ」値が他の数学の結果である場合、非常に小さいゼロ以外の数値を取得する可能性があり、unexpected結果を引き起こす可能性がありますあなたの部門の後。 「ゼロに近い」数値をゼロとして扱いたい場合は、許容値が適切です。ただし、これはアプリケーションと目標に完全に依存します。

コンパイラが 例外処理のためのIEEE 754標準 を使用している場合、ゼロで除算し、オーバーフローを引き起こすのに十分小さい値で除算すると、両方とも+ /の値になります-インフィニティ。これは、非常に小さな数のチェックを含めることを意味する場合があります(プラットフォームでオーバーフローが発生する可能性があります)。たとえば、- Windowsfloatおよびdoubleは両方とも仕様に準拠しており、非常に小さな除数がゼロのように+/-インフィニティを作成する可能性があります値。

コンパイラ/プラットフォームがIEEE 754浮動小数点標準に準拠していない場合、結果はプラットフォーム固有のものであると思われます。

3
Reed Copsey