web-dev-qa-db-ja.com

フロート比較を使用したゼロ除算の回避

ソースコードアナライザー(SonarQubeなど)は、フロート(またはダブル)の等価比較について不平を言います。比較される値は、しばしば分単位の丸め効果がある計算の結果である可能性があります。そのため、0.3 - 0.2 == 0.1はしばしばfalseを返しますが、数学的には常にtrueを返す必要があります(Python 2.7でテスト)。したがって、この不満は、潜在的に危険なコードについて警告するのに最適です。

このような状況の典型的なアプローチは、マージン、つまりイプシロンをチェックすることです。 g。

if abs(a - b) < epsilon then …

一方、除算が行われる前に除数がゼロと等しいかどうかをチェックすることにより、ゼロ除算の問題を回避するコードをよく目にすることがあります。

if divisor == 0.0 then
    // do some special handling like skipping the list element,
    // return 0.0 or whatever seems appropriate, depending on context
else
    result = divident / divisor
endif

これはdiv-by-zeroの問題を処理するようですが、スポット[divisor == 0.0]について不満を言うソースコードアナライザーには準拠していません。一見すると、アナライザーの問題のように見えます。誤検知のようです。 0.0のフロート等価チェックは許可されるべきですよね?

いくつかの検討の結果、除数がshouldの結果として0.0(0.3 - 0.2 - 0.1のような)になり、1e-17または0.00000000000000001

現在、これには2つの方法があります。

  1. 値は正確に0.0ではないため、除算が行われ、結果の値は「通常の」浮動小数点数になります(おそらく、1e200/1e-200はinfと見なされます)。それが起こると、呼び出し元は結果を処理する必要があります。

または

  1. されているはずでした 0.0、論理的にはisこの場合、コンピューターはそれに気付かないだけなので、ゼロのケースの特別な処理が行われる必要がありますこちらも。

2番目のオプションに投票した場合、イプシロンアプローチを使用して問題ありません。しかし、それはゼロっぽい値のように非常に小さい真のゼロ以外の値を扱います。 2つのケースを区別する方法はありません。

これにより、0.0に非常に近いこのような真の非ゼロ値を除算する必要があるのか​​、それともshouldをゼロの場合と同様に処理する(つまり、特別な処理を行う)かを検討します。結局のところ、そのような小さい値で除算すると、非常に大きい値になり、(グラフなどで)問題になることがよくあります。これは確かに状況次第であり、一般的に答えることはできません。

また、入力にゼロ(-ish)値が存在することが問題の原因ではなく、それ自体が単なる影響であるかどうかも検討しました。 e。おそらく、問題の根本はさらに深いところにあります。おそらく、浮動小数点数を想定し、それで除算することになっているアルゴリズムは、最初からゼロ(-ish)になる可能性のある値を受け取るべきではありません。

除算する前にゼロであるかどうかを確認する必要がある整数の使用例を考えることができます(たとえば、参照インデックスとの差が除数として使用されるインデックス、ある反復で両方が同じになる場合、差は0)ですが、float値がゼロっぽくなる可能性がある良い例は思いつきませんでした。たぶんそのようなことが起こった場合、それは単なる論理的なエラーでしたか?

だから、今私の質問は:

  1. 私の考慮事項に対処するゼロ除算の問題を回避するための浮動ゼロチェックのトピックに関する理論はありますか?それについてはインターネットでまだ何も見つかりませんでした。
  2. 誰かがコンテキストとその中のアルゴリズムの合理的な例を提供できますか?これは、ゼロになる可能性があり、それによって除算する必要がある浮動小数点値を期待します。そして、そのコンテキストに応じて、どのソリューション(イプシロン、純粋な== 0.0- check、おそらく別のアプローチ)を使用しますか?
6
Alfe

私はOPの個人的な理論に同意します。これは、コンピュータープログラムがゼロ除算演算を続行することを許可したり、除算の前に最小限のチェックのみを実行したりすることは通常の慣行ではないということです。

例外は、一般的すぎるものを実装している場合ですプログラマーとして)プログラミング言語(MATLABなど)が求めている数学的演算のコンテキスト/アプリケーション/ユースケース/物理的な意味がわからない場合実行します。これは、評価している数式が顧客から提供されたものであり、その数式の顧客の使用例がわからないためです。その場合は、InfやNaNなどの特別な表現をプレースホルダーとして使用します。

ただし、数式が統計ツールボックスの一部として提供されている場合は、状況が発生したときに説明を提供できるはずです。以下の「総重量がゼロの場合の加重平均」の例を参照してください。


除数アンダーフローテストを「反転」する方法があります。数学的には、bがゼロでない場合、および
abs(a) / abs(b) > abs(c)ここで、cは表現可能な最大の浮動小数点値であり、
abs(a) > abs(c) * abs(b)。 ---(ただし、実際にはそれよりも慎重な実装が必要です
(a, b)を渡して、除算がオーバーフローするか、アンダーフローするか、精度が悪いかを返す数学ライブラリ関数を見つけることができる場合があります。


ソースコードアナライザーは、コード内のパターンを探します。それらは、誰かの回避策ロジックがアプリケーションの設計目的に十分であるかどうかを決定するほど高度ではありません。 (実際には、平均的なプログラマーでさえ、その決定を下す資格がないかもしれません。)ソースコードアナライザーは、その決定を下す資格のある人で増強されることになっています。


ゼロの分母は、数式、無限級数(シーケンスの合計)など、多くの数学的操作で発生する可能性があります。分母がゼロに近づく(つまり、正確にゼロではないが、それよりも小さい)にもかかわらず、結果を計算する多くの数学的方法があります。マシンで表現可能な値)。これらは、式が逐語的に評価されないことを意味します-いくつかの微積分法を使用して変換され、各式に対していくつかの代替バージョンが選択される場合がありますゼロ除算の問題を回避するため。


データの加重平均では別の状況が発生します。データのサブセットを選択するクエリを実行した場合、次の場合:

  1. データのサブセットの重みの合計がゼロになる、または
  2. サブセットが実際に空の場合、つまりクエリが結果を返さない場合

次に、その状況を表現する適切な方法は、「クエリのサンプル(データ)が不十分」などです。


基本的な三角法では、一部の表現(勾配)は分割の問題に非常に敏感ですが、別の表現(方位、つまり角度)は敏感ではありません。たとえば、垂直線とほぼ垂直な線を水平線とほぼ水平な線と同じくらいロバストに表現する必要がある2D平面で線を表すには、次のようにします。

  1. 急なラインと急でないラインを切り替えます。 45度より急な線の場合、小さな数値による除算を回避するために、線の「反転」勾配として(x / y)ではなく(y / x)を使用します。
  2. a*x + b*y + c == 0などの代替表現を使用し、(a, b, c)が通常の場合は(a^2 + b^2)に、行がdegenerate(not-a-line)の場合は1.0に等しい必要があるという要件でパラメーター0.0を保存します。

degeneracyは、多くの異なるコンテキスト(およびコンテキスト固有の方法)では避けられないことに言及する価値があります。たとえば、ユーザーが "line" from point (x1, y1) to point (x2, y2)を渡して、その勾配を計算するように要求した場合、(x1 == x2 and y1 == y2)が発生すると、直線がないため、勾配はありません。単一の点しかないためです。ユーザーの入力。

4
rwong

質問1)私はそれを「理論」とは呼びませんが、0による除算または0の対数を取ることに関する統計的および科学的コンピューティングには、多くの実用的な知識があります。「理論」はありません。処理は完全にコンテキストに依存します。計算された量が正確に0に達することが完全に期待され、望ましい場合もあります。これは、ソリューションが完全に収束したことを示すだけの場合があります。それ以外の場合は、有効なドメインからアルゴリズムを適用しようとしている可能性があり、問題を再構成する必要があります。

質問2)統計で最も一般的な演算の1つは、確率を別の確率で割ってオッズ比を形成することです。これらの確率はどちらも任意に小さくできます。ただし、イベントの確率が正確に0であると主張することは、ほとんどの状況で不合理に強い主張であるため、問題を設定するときに、すべての入力確率に非常に小さい数を追加する場合があります。これは、絶対に不可能な出来事はないという「事前の」知識を表しています。これは、0による除算を防ぐ、またはオッズ比を計算するときに0の対数を取るという効果もあります。

3

これはかなり見当違いです。今日のほとんどの実装では、ゼロによる除算は無限大を与え、非常に非常に大きな数のように動作します。ゼロになる可能性のある商がある場合、それはゼロに非常に近くなり、次にその商は非常に大きくなり、問題があります-ゼロによる除算をチェックするかどうかにかかわらず、解決する必要があります。

Sin(x)/ xのような関数を計算する場合、分子と分母の両方が同時にゼロになる可能性がある場合、x == 0のテストは完全にうまくいきます(そして、その場合は明らかに1.0を返す必要がありますが、計算した場合(2 sin x)/ x、2.0を返す必要があります。x/ sin(x)を計算し、xがpiの倍数に近い場合、x = 0が正しくない場合を除いて、ゼロによる除算をチェックします。

1
gnasher729

a)ゼロによる除算が問題である場合、オーバーフローも問題です。

b)オーバーフローを防ぐためのチェックがある場合、ゼロによる除算を防ぐためのチェックは必要ありません。

c)ゼロによる除算を気にする必要はありません。

d)除算の前にif( divisor == 0)を実行することは、常に不必要または間違っています。

0
Brendan