web-dev-qa-db-ja.com

浮動小数点の等価性と許容誤差

丸めエラーのために_a_float == b_float_が_a_float / 3.0 * 3.0_と等しくない場合があるため、2つの浮動小数点数を_a_float_のようなもので比較すると問題が発生します。

通常は、fabs(a_float - b_float) < tolのように動作します。

どのようにtolを計算しますか?

理想的には、許容差は、最下位の数値の1つまたは2つの値よりもちょうど大きい必要があります。したがって、単精度浮動小数点数を使用する場合、_tol = 10E-6_はほぼ正しいはずです。ただし、これは、_a_float_が非常に小さいか非常に大きいという一般的なケースではうまく機能しません。

すべての一般的なケースについて、どのようにtolを正しく計算しますか?特にCまたはC++のケースに興味があります。

12
doron

このブログ投稿には、例、かなり簡単な実装、およびその背後にある詳細な理論が含まれています http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ これもシリーズの1つなので、いつでも詳細を読むことができます。つまり、ほとんどの数値にはULPを使用し、ゼロに近い数値にはイプシロンを使用しますが、まだ注意が必要です。浮動小数点演算について確認したい場合は、シリーズ全体を読むことをお勧めします。

17
aryan

私の知る限りでは、そうではありません。

正確さに対するアプリケーションの要件に依存する可能性があるため、一般的な「正しい答え」はありません。

たとえば、スクリーンピクセルで動作する2D物理シミュレーションは、1/4ピクセルで十分であると判断し、3D CAD原子力発電所の内部設計に使用されているシステムでは不十分な場合があります。

これをプログラムで外部から決定する方法がわかりません。

9
unwind

Cヘッダーファイル<float.h>は定数を提供しますFLT_EPSILONおよびDBL_EPSILON、これは、1.0と、float/doubleが表すことができる1.0より大きい最小数との差です。あなたはあなたの数のサイズとあなたが許容したい丸め誤差によってそれをスケーリングすることができます:

#include <float.h>
#ifndef DBL_TRUE_MIN
/* DBL_TRUE_MIN is a common non-standard extension for the minimum denorm value
 * DBL_MIN is the minimum non-denorm value -- use that if TRUE_MIN is not defined */
#define DBL_TRUE_MIN DBL_MIN
#endif

/* return the difference between |x| and the next larger representable double */
double dbl_epsilon(double x) {
    int exp;
    if (frexp(x, &exp) == 0.0)
        return DBL_TRUE_MIN;
    return ldexp(DBL_EPSILON, exp-1);
}
5
Chris Dodd

トラップ、スネア、抜け穴の世界へようこそ。他で言及されているように、浮動小数点の等価性と許容誤差の一般的な解決策はnotが存在します。とはいえ、プログラマーが特定のケースで使用できるツールと公理があります。

fabs(a_float - b_float) < tolには、「a_floatが非常に小さいか非常に大きい場合の一般的なケースではうまく機能しない」という言及された欠点があります。 fabs(a_float - ref_float) <= fabs(ref_float * tol)は、バリアントの範囲にはるかに対応しています。

OPの「単精度浮動小数点数はuse tol = 10E-6」はCとC++にとって少し気になるので、floatの算術演算をdoubleに簡単にプロモートすると、doubleではなくfloatの "トレランス"が機能します。 float f = 1.0; printf("%.20f\n", f/7.0);を検討する多くの新しいプログラマーは、_7.0_がdoubleの精度の計算を引き起こしたことを理解していません。大量のデータがdoubleより小さいサイズを必要とする場合を除いて、コード全体でfloatの使用をお勧めします。

C99はnextafter()を提供します。これは、「許容度」の測定に役立つ場合があります。これを使用して、次の表現可能な数を決定できます。これは、OPに役立ちます...「丸め誤差を許容するために、ストレージタイプの有効数字の完全な数字から1を引いたもの...」。 if ((nextafter(x, -INF) <= y && (y <= nextafter(x, +INF))) ...

使用されるtolまたは「許容範囲」のkindは、多くの場合問題の核心です。ほとんどの場合(IMHO)relative許容誤差が重要です。 e。 g。 「xとyは0.0001%以内ですか?」 absolute公差が必要な場合があります。例えば「xとyは0.0001以内ですか?」

許容値のvalueは、多くの場合、最善の値が状況に依存するため、議論の余地があります。 0.01以内で比較すると、ドルの金融アプリケーションでは機能しますが、円では機能しません。 (ヒント:簡単に更新できるコーディングスタイルを使用してください。)

4
chux

丸め誤差は、演算に使用する値によって異なります。

固定許容誤差の代わりに、おそらく次のようなイプシロンの係数を使用できます。

bool nearly_equal(double a, double b, int factor /* a factor of epsilon */)
{
  double min_a = a - (a - std::nextafter(a, std::numeric_limits<double>::lowest())) * factor;
  double max_a = a + (std::nextafter(a, std::numeric_limits<double>::max()) - a) * factor;

  return min_a <= b && max_a >= b;
}
1
Daniel Laügt

許容誤差の値は状況によって異なりますが、精度の比較を求める場合は、マシンのイプシロン値、numeric_limits :: epsilon()(ライブラリの制限)を許容誤差として使用できます。この関数は、1と、データ型で表現できる1より大きい最小値の差を返します。 http://msdn.Microsoft.com/en-us/library/6x7575x3.aspx

Floatまたはdoubleを比較している場合、イプシロンの値は異なります。たとえば、私のコンピューターでは、浮動小数点数を比較する場合、イプシロンの値は1.1920929e-007であり、倍数を比較する場合、イプシロンの値は2.2204460492503131e-016です。

Xとyの相対的な比較のために、イプシロンにxとyの最大絶対値を掛けます。

上記の結果は、ulps(最後の場所のユニット)を掛けることができ、正確にプレイすることができます。

#include <iostream>
#include <cmath>
#include <limits>

template<class T> bool are_almost_equal(T x, T y, int ulp)
{
    return std::abs(x-y) <= std::numeric_limits<T>::epsilon() * std::max(std::abs(x), std::abs(y)) * ulp
}
0
MartaF