221

今日、私はいくつかのC ++コード(他の誰かによって書かれた)を調べていて、このセクションを見つけました:

double someValue = ...
if (someValue <  std::numeric_limits<double>::epsilon() && 
    someValue > -std::numeric_limits<double>::epsilon()) {
  someValue = 0.0;
}

私はこれが理にかなっているのかどうかを理解しようとしています。

のドキュメントは次のようにepsilon()述べています。

この関数は、1と、[doubleで]表現可能な1より大きい最小値との差を返します。

これは0にも当てはまりますか?つまりepsilon()、最小値は0より大きいですか?0または、との間に数字があり、それは?0 + epsilonで表すことができますdoubleか?

そうでない場合、比較は?と同等ではありませんsomeValue == 0.0か?

4

11 に答える 11

203

64 ビットの IEEE double を想定すると、52 ビットの仮数部と 11 ビットの指数部があります。それをビットに分割しましょう:

1.0000 00000000 00000000 00000000 00000000 00000000 00000000 × 2^0 = 1

1より大きい最小の表現可能な数:

1.0000 00000000 00000000 00000000 00000000 00000000 00000001 × 2^0 = 1 + 2^-52

したがって:

epsilon = (1 + 2^-52) - 1 = 2^-52

0 からイプシロンまでの数字はありますか? たくさん...たとえば、最小の正の表現可能な(通常の)数は次のとおりです。

1.0000 00000000 00000000 00000000 00000000 00000000 00000000 × 2^-1022 = 2^-1022

実際(1022 - 52 + 1)×2^52 = 4372995238176751616、0 からイプシロンまでの数値があり、これは表現可能なすべての正の数値の 47% です...

于 2012-12-04T09:17:49.973 に答える
17

テストは確かに と同じではありませんsomeValue == 0。浮動小数点数の全体的な考え方は、指数と仮数を格納することです。したがって、これらは特定の数の精度の 2 進有効数字 (IEEE double の場合は 53) を持つ値を表します。表現可能な値は、1 付近よりも 0 付近の方がはるかに密集しています。

より使い慣れた 10 進法を使用するには、「有効数字 4 桁まで」の 10 進値を指数で格納するとします。次に、 より大きい次の表現可能な値11.001 * 10^0、およびepsilonです1.000 * 10^-3。しかし1.000 * 10^-4、指数が -4 を格納できると仮定すると、表現可能でもあります。IEEE double はの指数よりも小さい指数を格納できるという私の言葉を信じることができますepsilon

このコードだけでは、境界として具体的に使用することが理にかなっているのかどうかを判断することはできませんepsilon。コンテキストを確認する必要があります。これはepsilon、 を生成した計算の誤差の妥当な推定値であるsomeValue可能性がありますが、そうでない可能性もあります。

于 2012-12-04T08:52:16.453 に答える
13

0 とイプシロンの間に存在する数値があります。これは、イプシロンが 1 と 1 より大きく表現できる次の最大数との差であり、0 と 0 より大きく表現できる次の最大数との差ではないためです (もしそうであれば、コードはほとんど何もしません):-

#include <limits>

int main ()
{
  struct Doubles
  {
      double one;
      double epsilon;
      double half_epsilon;
  } values;

  values.one = 1.0;
  values.epsilon = std::numeric_limits<double>::epsilon();
  values.half_epsilon = values.epsilon / 2.0;
}

デバッガーを使用して、main の最後でプログラムを停止し、結果を確認すると、epsilon / 2 が epsilon の 0 と 1 とは異なることがわかります。

したがって、この関数は +/- イプシロンの間の値を取り、それらをゼロにします。

于 2012-12-04T09:11:52.723 に答える
5

数値(1.0、0.0、...)の周りのイプシロン(可能な限り最小の差)の近似は、次のプログラムで印刷できます。次の出力が出力されます。
epsilon for 0.0 is 4.940656e-324
epsilon for 1.0 is 2.220446e-16
少し考えてみると、指数はその数値のサイズに調整できるため、イプシロンの値を調べるために使用する数値が小さいほど、イプシロンは小さくなります。

#include <stdio.h>
#include <assert.h>
double getEps (double m) {
  double approx=1.0;
  double lastApprox=0.0;
  while (m+approx!=m) {
    lastApprox=approx;
    approx/=2.0;
  }
  assert (lastApprox!=0);
  return lastApprox;
}
int main () {
  printf ("epsilon for 0.0 is %e\n", getEps (0.0));
  printf ("epsilon for 1.0 is %e\n", getEps (1.0));
  return 0;
}
于 2012-12-04T09:37:14.160 に答える
4

Xと の次の値の差はXによって異なりXます。と の次の値の
epsilon()差のみです。と の次の値 の差はではありません。11
00epsilon()

代わりに、次のようにstd::nextafterdouble 値を比較するために使用できます。0

bool same(double a, double b)
{
  return std::nextafter(a, std::numeric_limits<double>::lowest()) <= b
    && std::nextafter(a, std::numeric_limits<double>::max()) >= b;
}

double someValue = ...
if (same (someValue, 0.0)) {
  someValue = 0.0;
}
于 2016-02-06T18:29:34.800 に答える
3

16 ビット レジスタに収まるおもちゃの浮動小数点数を扱っているとします。符号ビット、5 ビットの指数、および 10 ビットの仮数があります。

この浮動小数点数の値は、2 進数の 10 進値として解釈される仮数に 2 を乗じた指数です。

指数は 1 付近でゼロに等しくなります。したがって、仮数の最小桁は 1024 分の 1 です。

1/2 付近では指数がマイナス 1 になるため、仮数の最小部分は半分の大きさになります。5 ビットの指数では、マイナス 16 に達する可能性があり、その時点で、仮数の最小部分は 32m の 1 部分に相当します。そして、負の 16 指数では、値は約 32k の約 1 であり、上記で計算した約 1 のイプシロンよりもはるかにゼロに近くなります!

現在、これはおもちゃの浮動小数点モデルであり、実際の浮動小数点システムの癖をすべて反映しているわけではありませんが、イプシロンより小さい値を反映する機能は、実際の浮動小数点値とほぼ同じです。

于 2012-12-04T09:21:14.407 に答える
2

パソコンの精度次第だと思います。このを見てください: イプシロンが double で表されているが、精度が高い場合、比較は次と同等ではないことがわかります。

someValue == 0.0

とにかく良い質問です!

于 2012-12-04T08:50:32.263 に答える
2

仮数部と指数部があるため、これを 0 に適用することはできません。指数により、イプシロンよりも小さい非常に小さな数値を格納できますが、(1.0 - "非常に小さい数値") のようなことをしようとすると、1.0 になります。Epsilon は、値ではなく、仮数部にある値の精度の指標です。これは、格納できる数の正しい結果の 10 進数の桁数を示します。

于 2012-12-04T08:57:11.990 に答える
2

IEEE 浮動小数点では、最小のゼロ以外の正の値と最小のゼロ以外の負の値の間に、正のゼロと負のゼロの 2 つの値が存在します。値がゼロ以外の最小値の間にあるかどうかをテストすることは、ゼロと等しいかどうかをテストすることと同じです。ただし、割り当ては負のゼロを正のゼロに変更するため、影響を与える可能性があります。

浮動小数点形式は、最小の有限の正の値と負の値の間の 3 つの値 (正の無限小、符号なしゼロ、および負の無限小) を持つ可能性があると考えられます。私は実際にそのように動作する浮動小数点形式に精通していませんが、そのような動作は完全に合理的であり、IEEE の動作よりも間違いなく優れています (おそらく、それをサポートするために追加のハードウェアを追加する価値があるほど良くはありませんが、数学的には 1 /(1/INF)、1/(-1/INF)、および 1/(1-1) は、3 つの異なるゼロを示す 3 つの異なるケースを表す必要があります)。符号付きの無限小数が存在する場合、それをゼロと比較する必要があることを C 標準が義務付けているかどうかはわかりません。そうでない場合、上記のようなコードは、たとえば次のことを有効に保証できます。

于 2012-12-04T16:05:49.223 に答える
1

したがって、システムが 1.000000000000000000000 と 1.00000000000000000001 を区別できないとしましょう。つまり、1.0 と 1.0 + 1e-20 です。-1e-20 と +1e-20 の間で表現できる値がまだいくつかあると思いますか?

于 2012-12-04T08:50:36.327 に答える