614

double2 つまたは 2 つのfloat値を比較する最も効率的な方法は何ですか?

単純にこれを行うのは正しくありません:

bool CompareDoubles1 (double A, double B)
{
   return A == B;
}

しかし、次のようなもの:

bool CompareDoubles2 (double A, double B) 
{
   diff = A - B;
   return (diff < EPSILON) && (-diff < EPSILON);
}

無駄な処理をしているようです。

よりスマートなフロート比較器を知っている人はいますか?

4

33 に答える 33

520

他の提案の使用には細心の注意を払ってください。それはすべて文脈に依存します。

私は長い間、a==bもし|a-b|<epsilon. 根本的な問題は次のとおりです。

  1. a==bifとb==cthenというアルゴリズムにおける暗黙の推定a==c

  2. インチで測定された線とミル (.001 インチ) で測定された線に同じイプシロンを使用します。それはa==bしかし1000a!=1000b。(これが、AlmostEqual2sComplement がイプシロンまたは最大 ULPS を要求する理由です)。

  3. 角度の余弦と直線の長さの両方に同じイプシロンを使用!

  4. このような比較関数を使用して、コレクション内のアイテムを並べ替えます。(この場合、double に対して組み込みの C++ 演算子 == を使用すると、正しい結果が得られました。)

私が言ったように、それはすべてコンテキストと と の予想されるサイズに依存しaますb

ところで、std::numeric_limits<double>::epsilon()「マシンイプシロン」です。1.0 と double で表現できる次の値との差です。比較関数で使用できると思いますが、期待値が 1 未満の場合にのみ使用できます。(これは @cdv の回答への応答です...)

また、基本的にint算術演算がdoublesある場合 (ここでは、特定の場合に int 値を保持するために double を使用します)、算術演算は正しくなります。たとえば、4.0/2.0 は 1.0+1.0 と同じになります。これは、分数 (4.0/3.0) になるようなことをしないか、int のサイズを超えない限りです。

于 2008-09-16T22:06:07.353 に答える
194

イプシロン値との比較は、ほとんどの人が行うことです (ゲーム プログラミングでも)。

ただし、実装を少し変更する必要があります。

bool AreSame(double a, double b)
{
    return fabs(a - b) < EPSILON;
}

編集: Christer は、最近のブログ投稿で、このトピックに関するすばらしい情報の山を追加しました。楽しみ。

于 2008-08-20T02:14:51.013 に答える
140

浮動小数点数の比較は、コンテキストに依存します。操作の順序を変更しても結果が異なる可能性があるため、数値をどの程度「等しく」したいかを知ることが重要です。

Bruce Dawson による 浮動小数点数の比較は、浮動小数点数の比較を検討する際の出発点として適しています。

次の定義は、Knuth による The art of computer programmingからのものです。

bool approximatelyEqual(float a, float b, float epsilon)
{
    return fabs(a - b) <= ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}

bool essentiallyEqual(float a, float b, float epsilon)
{
    return fabs(a - b) <= ( (fabs(a) > fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}

bool definitelyGreaterThan(float a, float b, float epsilon)
{
    return (a - b) > ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}

bool definitelyLessThan(float a, float b, float epsilon)
{
    return (b - a) > ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}

もちろん、イプシロンの選択はコンテキストに依存し、数値をどの程度等しくするかを決定します。

浮動小数点数を比較するもう 1 つの方法は、数値の ULP (最下位の単位) を調べることです。特に比較を扱っているわけではありませんが、「すべてのコンピューター科学者が浮動小数点数について知っておくべきこと」という論文は、浮動小数点がどのように機能するか、および ULP とは何かを含め、落とし穴とは何かを理解するための優れたリソースです。

于 2008-10-31T15:13:42.363 に答える
120

Google C++ Testing Frameworkには、double と float の両方で機能する、AlmostEqual2sComplement の優れたクロスプラットフォーム テンプレート ベースの実装が含まれていることがわかりました。BSD ライセンスの下でリリースされていることを考えると、ライセンスを保持している限り、独自のコードで使用することは問題ありません。http://code.google.com/p/googletest/source/browse/trunk/include/gtest/internal/gtest-internal.h https://github.com/google/googletest/blobから以下のコードを抽出しました/master/googletest/include/gtest/internal/gtest-internal.h上にライセンスを追加しました。

必ず GTEST_OS_WINDOWS を何らかの値に #define してください (または、使用されているコードをコードベースに合ったものに変更してください - 結局は BSD ライセンスです)。

使用例:

double left  = // something
double right = // something
const FloatingPoint<double> lhs(left), rhs(right);

if (lhs.AlmostEquals(rhs)) {
  //they're equal!
}

コードは次のとおりです。

// Copyright 2005, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee)
//
// The Google C++ Testing Framework (Google Test)


// This template class serves as a compile-time function from size to
// type.  It maps a size in bytes to a primitive type with that
// size. e.g.
//
//   TypeWithSize<4>::UInt
//
// is typedef-ed to be unsigned int (unsigned integer made up of 4
// bytes).
//
// Such functionality should belong to STL, but I cannot find it
// there.
//
// Google Test uses this class in the implementation of floating-point
// comparison.
//
// For now it only handles UInt (unsigned int) as that's all Google Test
// needs.  Other types can be easily added in the future if need
// arises.
template <size_t size>
class TypeWithSize {
 public:
  // This prevents the user from using TypeWithSize<N> with incorrect
  // values of N.
  typedef void UInt;
};

// The specialization for size 4.
template <>
class TypeWithSize<4> {
 public:
  // unsigned int has size 4 in both gcc and MSVC.
  //
  // As base/basictypes.h doesn't compile on Windows, we cannot use
  // uint32, uint64, and etc here.
  typedef int Int;
  typedef unsigned int UInt;
};

// The specialization for size 8.
template <>
class TypeWithSize<8> {
 public:
#if GTEST_OS_WINDOWS
  typedef __int64 Int;
  typedef unsigned __int64 UInt;
#else
  typedef long long Int;  // NOLINT
  typedef unsigned long long UInt;  // NOLINT
#endif  // GTEST_OS_WINDOWS
};


// This template class represents an IEEE floating-point number
// (either single-precision or double-precision, depending on the
// template parameters).
//
// The purpose of this class is to do more sophisticated number
// comparison.  (Due to round-off error, etc, it's very unlikely that
// two floating-points will be equal exactly.  Hence a naive
// comparison by the == operation often doesn't work.)
//
// Format of IEEE floating-point:
//
//   The most-significant bit being the leftmost, an IEEE
//   floating-point looks like
//
//     sign_bit exponent_bits fraction_bits
//
//   Here, sign_bit is a single bit that designates the sign of the
//   number.
//
//   For float, there are 8 exponent bits and 23 fraction bits.
//
//   For double, there are 11 exponent bits and 52 fraction bits.
//
//   More details can be found at
//   http://en.wikipedia.org/wiki/IEEE_floating-point_standard.
//
// Template parameter:
//
//   RawType: the raw floating-point type (either float or double)
template <typename RawType>
class FloatingPoint {
 public:
  // Defines the unsigned integer type that has the same size as the
  // floating point number.
  typedef typename TypeWithSize<sizeof(RawType)>::UInt Bits;

  // Constants.

  // # of bits in a number.
  static const size_t kBitCount = 8*sizeof(RawType);

  // # of fraction bits in a number.
  static const size_t kFractionBitCount =
    std::numeric_limits<RawType>::digits - 1;

  // # of exponent bits in a number.
  static const size_t kExponentBitCount = kBitCount - 1 - kFractionBitCount;

  // The mask for the sign bit.
  static const Bits kSignBitMask = static_cast<Bits>(1) << (kBitCount - 1);

  // The mask for the fraction bits.
  static const Bits kFractionBitMask =
    ~static_cast<Bits>(0) >> (kExponentBitCount + 1);

  // The mask for the exponent bits.
  static const Bits kExponentBitMask = ~(kSignBitMask | kFractionBitMask);

  // How many ULP's (Units in the Last Place) we want to tolerate when
  // comparing two numbers.  The larger the value, the more error we
  // allow.  A 0 value means that two numbers must be exactly the same
  // to be considered equal.
  //
  // The maximum error of a single floating-point operation is 0.5
  // units in the last place.  On Intel CPU's, all floating-point
  // calculations are done with 80-bit precision, while double has 64
  // bits.  Therefore, 4 should be enough for ordinary use.
  //
  // See the following article for more details on ULP:
  // http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm.
  static const size_t kMaxUlps = 4;

  // Constructs a FloatingPoint from a raw floating-point number.
  //
  // On an Intel CPU, passing a non-normalized NAN (Not a Number)
  // around may change its bits, although the new value is guaranteed
  // to be also a NAN.  Therefore, don't expect this constructor to
  // preserve the bits in x when x is a NAN.
  explicit FloatingPoint(const RawType& x) { u_.value_ = x; }

  // Static methods

  // Reinterprets a bit pattern as a floating-point number.
  //
  // This function is needed to test the AlmostEquals() method.
  static RawType ReinterpretBits(const Bits bits) {
    FloatingPoint fp(0);
    fp.u_.bits_ = bits;
    return fp.u_.value_;
  }

  // Returns the floating-point number that represent positive infinity.
  static RawType Infinity() {
    return ReinterpretBits(kExponentBitMask);
  }

  // Non-static methods

  // Returns the bits that represents this number.
  const Bits &bits() const { return u_.bits_; }

  // Returns the exponent bits of this number.
  Bits exponent_bits() const { return kExponentBitMask & u_.bits_; }

  // Returns the fraction bits of this number.
  Bits fraction_bits() const { return kFractionBitMask & u_.bits_; }

  // Returns the sign bit of this number.
  Bits sign_bit() const { return kSignBitMask & u_.bits_; }

  // Returns true iff this is NAN (not a number).
  bool is_nan() const {
    // It's a NAN if the exponent bits are all ones and the fraction
    // bits are not entirely zeros.
    return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0);
  }

  // Returns true iff this number is at most kMaxUlps ULP's away from
  // rhs.  In particular, this function:
  //
  //   - returns false if either number is (or both are) NAN.
  //   - treats really large numbers as almost equal to infinity.
  //   - thinks +0.0 and -0.0 are 0 DLP's apart.
  bool AlmostEquals(const FloatingPoint& rhs) const {
    // The IEEE standard says that any comparison operation involving
    // a NAN must return false.
    if (is_nan() || rhs.is_nan()) return false;

    return DistanceBetweenSignAndMagnitudeNumbers(u_.bits_, rhs.u_.bits_)
        <= kMaxUlps;
  }

 private:
  // The data type used to store the actual floating-point number.
  union FloatingPointUnion {
    RawType value_;  // The raw floating-point number.
    Bits bits_;      // The bits that represent the number.
  };

  // Converts an integer from the sign-and-magnitude representation to
  // the biased representation.  More precisely, let N be 2 to the
  // power of (kBitCount - 1), an integer x is represented by the
  // unsigned number x + N.
  //
  // For instance,
  //
  //   -N + 1 (the most negative number representable using
  //          sign-and-magnitude) is represented by 1;
  //   0      is represented by N; and
  //   N - 1  (the biggest number representable using
  //          sign-and-magnitude) is represented by 2N - 1.
  //
  // Read http://en.wikipedia.org/wiki/Signed_number_representations
  // for more details on signed number representations.
  static Bits SignAndMagnitudeToBiased(const Bits &sam) {
    if (kSignBitMask & sam) {
      // sam represents a negative number.
      return ~sam + 1;
    } else {
      // sam represents a positive number.
      return kSignBitMask | sam;
    }
  }

  // Given two numbers in the sign-and-magnitude representation,
  // returns the distance between them as an unsigned number.
  static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits &sam1,
                                                     const Bits &sam2) {
    const Bits biased1 = SignAndMagnitudeToBiased(sam1);
    const Bits biased2 = SignAndMagnitudeToBiased(sam2);
    return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1);
  }

  FloatingPointUnion u_;
};

編集: この投稿は 4 年前のものです。それはおそらくまだ有効で、コードは素晴らしいですが、改善を見つけた人もいます。AlmostEqualsここに貼り付けたものではなく、Google Test のソース コードから最新バージョンを入手するのが最善です。

于 2010-08-06T11:24:55.763 に答える
50

より詳細なアプローチについては、浮動小数点数の比較を参照してください。そのリンクからのコード スニペットは次のとおりです。

// Usable AlmostEqual function    
bool AlmostEqual2sComplement(float A, float B, int maxUlps)    
{    
    // Make sure maxUlps is non-negative and small enough that the    
    // default NAN won't compare as equal to anything.    
    assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024);    
    int aInt = *(int*)&A;    
    // Make aInt lexicographically ordered as a twos-complement int    
    if (aInt < 0)    
        aInt = 0x80000000 - aInt;    
    // Make bInt lexicographically ordered as a twos-complement int    
    int bInt = *(int*)&B;    
    if (bInt < 0)    
        bInt = 0x80000000 - bInt;    
    int intDiff = abs(aInt - bInt);    
    if (intDiff <= maxUlps)    
        return true;    
    return false;    
}
于 2008-08-20T06:31:10.773 に答える
34

これが古いスレッドであることを認識していますが、この記事は浮動小数点数の比較で見つけた最も単純な記事の1つであり、さらに詳しく調べたい場合は、より詳細な参照もあり、メインサイトは問題の完全な範囲をカバーしています浮動小数点数の取り扱い 浮動小数点ガイド:比較.

Floating-point tolerances revisitedには、もう少し実用的な記事があり、C++ で要約すると絶対許容範囲テストがあることに注意してください。

bool absoluteToleranceCompare(double x, double y)
{
    return std::fabs(x - y) <= std::numeric_limits<double>::epsilon() ;
}

および相対許容テスト:

bool relativeToleranceCompare(double x, double y)
{
    double maxXY = std::max( std::fabs(x) , std::fabs(y) ) ;
    return std::fabs(x - y) <= std::numeric_limits<double>::epsilon()*maxXY ;
}

この記事では、xyが大きい場合は絶対テストが失敗し、小さい場合は相対テストで失敗することを指摘しています。絶対許容誤差と相対許容誤差が同じであると仮定すると、組み合わせたテストは次のようになります。

bool combinedToleranceCompare(double x, double y)
{
    double maxXYOne = std::max( { 1.0, std::fabs(x) , std::fabs(y) } ) ;

    return std::fabs(x - y) <= std::numeric_limits<double>::epsilon()*maxXYOne ;
}
于 2013-02-21T21:42:53.007 に答える
27

C++ でイプシロンを取得する移植可能な方法は次のとおりです。

#include <limits>
std::numeric_limits<double>::epsilon()

次に、比較関数は次のようになります

#include <cmath>
#include <limits>

bool AreSame(double a, double b) {
    return std::fabs(a - b) < std::numeric_limits<double>::epsilon();
}
于 2008-09-01T09:59:55.200 に答える
15

あなたが書いたコードにはバグがあります:

return (diff < EPSILON) && (-diff > EPSILON);

正しいコードは次のとおりです。

return (diff < EPSILON) && (diff > -EPSILON);

(...はい、これは異なります)

場合によっては、ファブによって遅延評価が失われることはないのではないでしょうか。コンパイラに依存すると思います。両方試してみるのもいいかもしれません。それらが平均的に同等である場合は、ファブで実装してください。

2 つのフロートのどちらが他のフロートよりも大きい可能性が高いかについての情報がある場合は、比較の順序で再生して、遅延評価をより有効に活用できます。

最後に、この関数をインライン化すると、より良い結果が得られる場合があります。あまり改善されそうにありませんが…

編集: OJ、コードを修正していただきありがとうございます。それに伴いコメントを削除しました

于 2008-08-20T04:32:25.537 に答える
14

`return fabs(a --b)<EPSILON;

次の場合は問題ありません。

  • 入力の大きさの順序はあまり変わりません
  • 非常に少数の反対の符号は等しいものとして扱うことができます

しかし、そうでなければ、それはあなたをトラブルに導くでしょう。倍精度数の解像度は小数点以下16桁です。比較している2つの数値がEPSILON*1.0E16よりも大きい場合は、次のように言った方がよいでしょう。

return a==b;

最初の問題について心配する必要があり、2番目の問題がアプリケーションに問題ないと想定する別のアプローチを検討します。解決策は次のようになります。

#define VERYSMALL  (1.0E-150)
#define EPSILON    (1.0E-8)
bool AreSame(double a, double b)
{
    double absDiff = fabs(a - b);
    if (absDiff < VERYSMALL)
    {
        return true;
    }

    double maxAbs  = max(fabs(a) - fabs(b));
    return (absDiff/maxAbs) < EPSILON;
}

これは計算コストが高くなりますが、必要な場合もあります。これは、エンジニアリングライブラリを扱っており、入力が数十桁異なる可能性があるため、私の会社で行う必要があることです。

とにかく、要点はこれです(そして事実上すべてのプログラミング問題に当てはまります):あなたのニーズが何であるかを評価し、次にあなたのニーズに対処するための解決策を考え出します-簡単な答えがあなたのニーズに対処するとは思わないでください。評価の結果、fabs(a-b) < EPSILONそれで十分であることがわかった場合は、完璧です。それを使用してください。ただし、その欠点やその他の考えられる解決策にも注意してください。

于 2008-09-01T08:00:56.937 に答える
9

他の人が指摘したように、固定指数イプシロン (0.0000001 など) を使用しても、イプシロン値から離れた値には役に立ちません。たとえば、2 つの値が 10000.000977 と 10000 の場合これら 2 つの数値の間に 32 ビットの浮動小数点値はありません。ここで、0.0009 未満のイプシロンは無意味です。単純な等値演算子を使用することもできます。

同様に、2 つの値のサイズがイプシロンに近づくと、相対誤差は 100% になります。

したがって、0.00001 などの固定小数点数と浮動小数点値 (指数は任意) を混在させようとしても無意味です。これは、オペランド値が狭い領域内 (つまり、特定の指数に近い) にあることが保証されている場合、およびその特定のテストに対してイプシロン値を適切に選択した場合にのみ機能します。空から数値を引き出すと (「ねえ! 0.00001 は小さいから、いいに違いない!」)、数値エラーが発生する運命にあります。私は、悪い数値コードのデバッグに多くの時間を費やしました。このコードでは、一部の貧弱なシュマックがランダムなイプシロン値を投げて、さらに別のテスト ケースを機能させています。

何らかの数値プログラミングを行っていて、固定小数点のイプシロンに到達する必要があると思われる場合は、BRUCE'S ARTICLE ON COMPARING FLOATING-POINT NUMBERS を読んでください

浮動小数点数の比較

于 2011-09-12T22:48:55.793 に答える
4

残念ながら、「無駄な」コードでさえ正しくありません。EPSILONは、 1.0に追加してその値を変更できる最小の値です。値1.0は非常に重要です。EPSILONに追加しても、数値が大きくても変化しません。これで、この値を比較している数値にスケーリングして、それらが異なるかどうかを判断できます。2つのdoubleを比較するための正しい式は次のとおりです。

if (fabs(a - b) <= DBL_EPSILON * fmax(fabs(a), fabs(b)))
{
    // ...
}

これは最低限です。ただし、一般的には、計算でノイズを考慮し、最下位ビットのいくつかを無視する必要があるため、より現実的な比較は次のようになります。

if (fabs(a - b) <= 16 * DBL_EPSILON * fmax(fabs(a), fabs(b)))
{
    // ...
}

比較パフォーマンスが非常に重要であり、値の範囲がわかっている場合は、代わりに固定小数点数を使用する必要があります。

于 2011-07-29T13:48:17.700 に答える
3

浮動小数点数の汎用的な比較は、一般的に無意味です。どのように比較するかは、目前の問題によって異なります。多くの問題では、数値は十分に離散化されているため、特定の許容範囲内で数値を比較できます。残念なことに、このようなトリックが実際には機能しない問題も数多くあります。一例として、観測値が障壁に非常に近い場合に、問題の数値 (デジタル ストック オプションが思い浮かびます) の Heaviside (ステップ) 関数を使用することを検討してください。許容範囲に基づいた比較を実行しても、問題が元のバリアから 2 つの新しいバリアに効果的にシフトするため、あまり効果がありません。繰り返しますが、このような問題に対する汎用的な解決策はなく、特定の解決策では、安定性を達成するために数値的手法を変更する必要がある場合があります。

于 2008-08-28T11:55:52.627 に答える
2

以前に投稿された回答に基づく私のクラス。Google のコードと非常に似ていますが、すべての NaN 値を 0xFF000000 より上にプッシュするバイアスを使用しています。これにより、NaN のチェックが高速になります。

このコードは、概念を示すためのものであり、一般的なソリューションではありません。Google のコードは、すべてのプラットフォーム固有の値を計算する方法を既に示しており、そのすべてを複製したくありませんでした。このコードで限定的なテストを行いました。

typedef unsigned int   U32;
//  Float           Memory          Bias (unsigned)
//  -----           ------          ---------------
//   NaN            0xFFFFFFFF      0xFF800001
//   NaN            0xFF800001      0xFFFFFFFF
//  -Infinity       0xFF800000      0x00000000 ---
//  -3.40282e+038   0xFF7FFFFF      0x00000001    |
//  -1.40130e-045   0x80000001      0x7F7FFFFF    |
//  -0.0            0x80000000      0x7F800000    |--- Valid <= 0xFF000000.
//   0.0            0x00000000      0x7F800000    |    NaN > 0xFF000000
//   1.40130e-045   0x00000001      0x7F800001    |
//   3.40282e+038   0x7F7FFFFF      0xFEFFFFFF    |
//   Infinity       0x7F800000      0xFF000000 ---
//   NaN            0x7F800001      0xFF000001
//   NaN            0x7FFFFFFF      0xFF7FFFFF
//
//   Either value of NaN returns false.
//   -Infinity and +Infinity are not "close".
//   -0 and +0 are equal.
//
class CompareFloat{
public:
    union{
        float     m_f32;
        U32       m_u32;
    };
    static bool   CompareFloat::IsClose( float A, float B, U32 unitsDelta = 4 )
                  {
                      U32    a = CompareFloat::GetBiased( A );
                      U32    b = CompareFloat::GetBiased( B );

                      if ( (a > 0xFF000000) || (b > 0xFF000000) )
                      {
                          return( false );
                      }
                      return( (static_cast<U32>(abs( a - b ))) < unitsDelta );
                  }
    protected:
    static U32    CompareFloat::GetBiased( float f )
                  {
                      U32    r = ((CompareFloat*)&f)->m_u32;

                      if ( r & 0x80000000 )
                      {
                          return( ~r - 0x007FFFFF );
                      }
                      return( r + 0x7F800000 );
                  }
};
于 2012-01-26T21:01:16.783 に答える
2

float は整数型のように完全に比較できないため、浮動小数点比較のためにこの処理を行う必要があります。さまざまな比較演算子の関数を次に示します。

==( )に等しい浮動小数点

fabs()また、 orに依存するよりも減算手法を好みabs()ますが、パフォーマンスに大きな違いがあるかどうかを実際に確認するには、64 ビット PC から ATMega328 マイクロコントローラー (Arduino) までのさまざまなアーキテクチャで高速プロファイルする必要があります。

では、この絶対値のことはすべて忘れて、減算と比較だけを行いましょう。

ここでマイクロソフトの例から変更:

/// @brief      See if two floating point numbers are approximately equal.
/// @param[in]  a        number 1
/// @param[in]  b        number 2
/// @param[in]  epsilon  A small value such that if the difference between the two numbers is
///                      smaller than this they can safely be considered to be equal.
/// @return     true if the two numbers are approximately equal, and false otherwise
bool is_float_eq(float a, float b, float epsilon) {
    return ((a - b) < epsilon) && ((b - a) < epsilon);
}
bool is_double_eq(double a, double b, double epsilon) {
    return ((a - b) < epsilon) && ((b - a) < epsilon);
}

使用例:

constexpr float EPSILON = 0.0001; // 1e-4
is_float_eq(1.0001, 0.99998, EPSILON);

完全にはわかりませんが、この非常に支持された回答の下のコメントで説明されているように、イプシロンベースのアプローチに対する批判のいくつかは、浮動小数点に従ってスケーリングされた変数イプシロンを使用することで解決できるようです。次のように値が比較されます。

float a = 1.0001;
float b = 0.99998;
float epsilon = std::max(std::fabs(a), std::fabs(b)) * 1e-4;

is_float_eq(a, b, epsilon);

このように、イプシロン値は浮動小数点値に合わせてスケーリングされるため、重要でなくなるほど小さい値になることはありません。

完全を期すために、残りを追加しましょう。

( )より大きい、( >) より小さい<:

/// @brief      See if floating point number `a` is > `b`
/// @param[in]  a        number 1
/// @param[in]  b        number 2
/// @param[in]  epsilon  a small value such that if `a` is > `b` by this amount, `a` is considered
///             to be definitively > `b`
/// @return     true if `a` is definitively > `b`, and false otherwise
bool is_float_gt(float a, float b, float epsilon) {
    return a > b + epsilon;
}
bool is_double_gt(double a, double b, double epsilon) {
    return a > b + epsilon;
}

/// @brief      See if floating point number `a` is < `b`
/// @param[in]  a        number 1
/// @param[in]  b        number 2
/// @param[in]  epsilon  a small value such that if `a` is < `b` by this amount, `a` is considered
///             to be definitively < `b`
/// @return     true if `a` is definitively < `b`, and false otherwise
bool is_float_lt(float a, float b, float epsilon) {
    return a < b - epsilon;
}
bool is_double_lt(double a, double b, double epsilon) {
    return a < b - epsilon;
}

( ) 以上 ( )>=以下<=

/// @brief      Returns true if `a` is definitively >= `b`, and false otherwise
bool is_float_ge(float a, float b, float epsilon) {
    return a > b - epsilon;
}
bool is_double_ge(double a, double b, double epsilon) {
    return a > b - epsilon;
}

/// @brief      Returns true if `a` is definitively <= `b`, and false otherwise
bool is_float_le(float a, float b, float epsilon) {
    return a < b + epsilon;
}
bool is_double_le(double a, double b, double epsilon) {
    return a < b + epsilon;
}

その他の改善:

  1. epsilonC++ での適切なデフォルト値は で、これはまたは、、またはのstd::numeric_limits<T>::epsilon()いずれかに評価されます。ここを参照してください: https://en.cppreference.com/w/cpp/types/numeric_limits/epsilon . 、、およびのヘッダーも表示されます。 0FLT_EPSILONDBL_EPSILONLDBL_EPSILONfloat.hFLT_EPSILONDBL_EPSILONLDBL_EPSILON
    1. https://en.cppreference.com/w/cpp/header/cfloatおよび_
    2. https://www.cplusplus.com/reference/cfloat/
  2. 代わりに関数をテンプレート化して、すべての浮動小数点型を処理することができます: float、、doubleおよび。テンプレート内のa を介してこれらの型の型チェックを行います。long doublestatic_assert()
  3. 値をスケーリングすることは、非常に大きな値とepsilon非常に小さな値に対して機能することを保証するための良い考えです。http://realtimecollisiondetection.net/blog/?p=89の記事で推奨および説明されています。そのため、その記事で説明されているように、 に等しいスケーリング値でイプシロンをスケーリングする必要があります。そうしないと、大きさが大きくなるにつれて、イプシロンは最終的にそれらの値に比べて非常に小さくなり、浮動小数点エラーで失われます。そのため、それらのように大きさが大きくなるようにスケーリングします。ただし、イプシロンの最小許容スケーリング係数として使用すると、非常に小さいマグニチュードとabmax(1.0, abs(a), abs(b))ab1.0abイプシロン自体はあまり小さくスケーリングされないため、浮動小数点エラーで失われます。したがって、最小倍率を に制限し1.0ます。
  4. 上記の関数をクラスに「カプセル化」したい場合は、そうしないでください。代わりに、必要に応じて名前空間にラップして、名前空間にします。例: すべてのスタンドアロン関数を という名前空間に入れると、次のように関数にfloat_comparisonアクセスできます。is_eq()float_comparison::is_eq(1.0, 1.5);
  5. 2 つの値の比較だけでなく、ゼロとの比較を追加するのもよいでしょう。
  6. したがって、上記の改善を適用した、より優れたタイプのソリューションを次に示します。
    namespace float_comparison {
    
    /// Scale the epsilon value to become large for large-magnitude a or b, 
    /// but no smaller than 1.0, per the explanation above, to ensure that 
    /// epsilon doesn't ever fall out in floating point error as a and/or b
    /// increase in magnitude.
    template<typename T>
    static constexpr T scale_epsilon(T a, T b, T epsilon = 
        std::numeric_limits<T>::epsilon()) noexcept 
    {
        static_assert(std::is_floating_point_v<T>, "Floating point comparisons "
            "require type float, double, or long double.");
        T scaling_factor;
        // Special case for when a or b is infinity
        if (std::isinf(a) || std::isinf(b)) 
        {
            scaling_factor = 0;
        } 
        else 
        {
            scaling_factor = std::max({(T)1.0, std::abs(a), std::abs(b)});
        }
    
        T epsilon_scaled = scaling_factor * std::abs(epsilon);
        return epsilon_scaled;
    }
    
    // Compare two values
    
    /// Equal: returns true if a is approximately == b, and false otherwise
    template<typename T>
    static constexpr bool is_eq(T a, T b, T epsilon = 
        std::numeric_limits<T>::epsilon()) noexcept 
    {
        static_assert(std::is_floating_point_v<T>, "Floating point comparisons "
            "require type float, double, or long double.");
        // test `a == b` first to see if both a and b are either infinity 
        // or -infinity
        return a == b || std::abs(a - b) <= scale_epsilon(a, b, epsilon);
    }
    
    /* 
    etc. etc.:
    is_eq()
    is_ne()
    is_lt()
    is_le()
    is_gt()
    is_ge()
    */
    
    // Compare against zero
    
    /// Equal: returns true if a is approximately == 0, and false otherwise
    template<typename T>
    static constexpr bool is_eq_zero(T a, T epsilon = 
        std::numeric_limits<T>::epsilon()) noexcept 
    {
        static_assert(std::is_floating_point_v<T>, "Floating point comparisons "
            "require type float, double, or long double.");
        return is_eq(a, (T)0.0, epsilon);
    }
    
    /* 
    etc. etc.:
    is_eq_zero()
    is_ne_zero()
    is_lt_zero()
    is_le_zero()
    is_gt_zero()
    is_ge_zero()
    */
    
    } // namespace float_comparison
    

以下も参照してください。

  1. 上記のいくつかの関数のマクロ形式は、私のレポ: utility.hにあります。
    1. 2020 年 11 月 29 日更新: これは進行中の作業であり、準備ができたら別の回答にする予定ですが、このファイルの C のすべての関数のより優れたスケーリングされたイプシロン バージョンを作成しました。 : utility.c。見てください。
  2. 私が今しなければならない追加の読み物: Floating-point tolerances revisited, by Christer Ericson . 非常に役立つ記事!a本当に大きなマグニチュードや値であっても、浮動小数点エラーに陥らないようにするためにイプシロンをスケーリングすることについて話していbます!
于 2020-11-26T02:28:05.163 に答える
1

浮動小数点の減算を含むこれらの回答には非常に注意が必要です(例:fabs(ab)<イプシロン)。まず、浮動小数点数は、大きさが大きく、間隔がイプシロンよりも大きい十分に高い大きさでよりまばらになります。a==bを実行するだけでもかまいません。次に、2つの非常に近い浮動小数点数を引くと(ほぼ等しいものを探している場合、これらはそうなる傾向があります)、まさに壊滅的なキャンセルを得る方法です。

移植性はありませんが、gromの答えはこれらの問題を回避するのに最適だと思います。

于 2010-08-08T00:59:20.333 に答える
0

実際、数値計算ソフトウェアでは、2 つの浮動小数点数が正確に等しいかどうかを確認したい場合があります。これを同様の質問に投稿しました

https://stackoverflow.com/a/10973098/1447411

したがって、「CompareDoubles1」が一般的に間違っているとは言えません。

于 2012-06-11T00:25:54.547 に答える
0

量のスケールに関して:

が何らかのepsilon物理的な意味での量 (すなわち相対値) のごく一部でありABタイプが同じ意味で比較可能である場合、次のことはまったく正しいと思います。

#include <limits>
#include <iomanip>
#include <iostream>

#include <cmath>
#include <cstdlib>
#include <cassert>

template< typename A, typename B >
inline
bool close_enough(A const & a, B const & b,
                  typename std::common_type< A, B >::type const & epsilon)
{
    using std::isless;
    assert(isless(0, epsilon)); // epsilon is a part of the whole quantity
    assert(isless(epsilon, 1));
    using std::abs;
    auto const delta = abs(a - b);
    auto const x = abs(a);
    auto const y = abs(b);
    // comparable generally and |a - b| < eps * (|a| + |b|) / 2
    return isless(epsilon * y, x) && isless(epsilon * x, y) && isless((delta + delta) / (x + y), epsilon);
}

int main()
{
    std::cout << std::boolalpha << close_enough(0.9, 1.0, 0.1) << std::endl;
    std::cout << std::boolalpha << close_enough(1.0, 1.1, 0.1) << std::endl;
    std::cout << std::boolalpha << close_enough(1.1,    1.2,    0.01) << std::endl;
    std::cout << std::boolalpha << close_enough(1.0001, 1.0002, 0.01) << std::endl;
    std::cout << std::boolalpha << close_enough(1.0, 0.01, 0.1) << std::endl;
    return EXIT_SUCCESS;
}
于 2013-08-29T18:06:53.030 に答える
0

別の興味深い実装が見つかりました: https://en.cppreference.com/w/cpp/types/numeric_limits/epsilon

#include <cmath>
#include <limits>
#include <iomanip>
#include <iostream>
#include <type_traits>
#include <algorithm>



template<class T>
typename std::enable_if<!std::numeric_limits<T>::is_integer, bool>::type
    almost_equal(T x, T y, int ulp)
{
    // the machine epsilon has to be scaled to the magnitude of the values used
    // and multiplied by the desired precision in ULPs (units in the last place)
    return std::fabs(x-y) <= std::numeric_limits<T>::epsilon() * std::fabs(x+y) * ulp
        // unless the result is subnormal
        || std::fabs(x-y) < std::numeric_limits<T>::min();
}

int main()
{
    double d1 = 0.2;
    double d2 = 1 / std::sqrt(5) / std::sqrt(5);
    std::cout << std::fixed << std::setprecision(20) 
        << "d1=" << d1 << "\nd2=" << d2 << '\n';

    if(d1 == d2)
        std::cout << "d1 == d2\n";
    else
        std::cout << "d1 != d2\n";

    if(almost_equal(d1, d2, 2))
        std::cout << "d1 almost equals d2\n";
    else
        std::cout << "d1 does not almost equal d2\n";
}
于 2019-12-19T02:20:31.947 に答える
-1

比較をどの程度正確にするかによって異なります。まったく同じ数を比較したい場合は、==を使用してください。(実際にまったく同じ番号が必要な場合を除いて、これを実行することはほとんどありません。)適切なプラットフォームでは、次のことも実行できます。

diff= a - b; return fabs(diff)<EPSILON;

fabsかなり速い傾向があるように。かなり高速とは、基本的にビット単位のANDであるため、高速である方がよいということです。

また、doubleとfloatを比較するための整数のトリックは優れていますが、さまざまなCPUパイプラインを効果的に処理することがより困難になる傾向があります。また、スタックを頻繁に使用される値の一時ストレージ領域として使用するため、最近の特定の順序付けアーキテクチャでは明らかに高速ではありません。(気になる人のためのロードヒットストア。)

于 2008-08-20T16:11:59.240 に答える
-1

double固定されたと 2 つを比較することはできませんEPSILON。の値によってdouble異なりEPSILONます。

より良い二重比較は次のようになります。

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;
}
于 2016-02-06T18:07:53.520 に答える
-1
/// testing whether two doubles are almost equal. We consider two doubles
/// equal if the difference is within the range [0, epsilon).
///
/// epsilon: a positive number (supposed to be small)
///
/// if either x or y is 0, then we are comparing the absolute difference to
/// epsilon.
/// if both x and y are non-zero, then we are comparing the relative difference
/// to epsilon.
bool almost_equal(double x, double y, double epsilon)
{
    double diff = x - y;
    if (x != 0 && y != 0){
        diff = diff/y; 
    }

    if (diff < epsilon && -1.0*diff < epsilon){
        return true;
    }
    return false;
}

この関数を小さなプロジェクトに使用しましたが、機能しますが、次の点に注意してください。

倍精度誤差は、あなたを驚かせる可能性があります。イプシロン = 1.0e-6 の場合、上記のコードによれば 1.0 と 1.000001 は等しいと見なされるべきではありませんが、私のマシンでは、関数はそれらが等しいと見なされます。これは、1.000001 をバイナリ形式に正確に変換できないためです。おそらく 1.0000009xxx です。1.0 と 1.0000011 でテストしたところ、今回は期待どおりの結果が得られました。

于 2014-03-04T02:58:31.380 に答える
-3

編集:このバージョンでは、数値が互いに異なることを確認しますが、一部の分数(0.0001%など)よりも大きく異なります:

bool floatApproximatelyEquals(const float a, const float b) {
    if (b == 0.) return a == 0.; // preventing division by zero
    return abs(1. - a / b) < 1e-6;
}

float の分数制限の可能性に関するSneftelのコメントに注意してください。

また、絶対イプシロンを使用したアプローチとは異なることに注意してください-ここでは「大きさの順序」について気にしません-数値は、たとえば1e100、またはである可能性があり1e-100、それらは常に一貫して比較され、イプシロンを更新する必要はありませんすべての場合。


古い答え: 次の方法では、システムに依存する 2 つの値の「文字列表現」を比較しています (この場合はフロート)。同様に、両方を印刷して、同じように見えるかどうかを目で確認します。

#include <iostream>
#include <string>

bool floatApproximatelyEquals(const float a, const float b) {
    return std::to_string(a) == std::to_string(b);
}

手順:

  • 数値係数 (またはべき乗) が効果的に考慮されるため、数値が 1.2、1.2e345678、0.00000123、1.2e-345678 (絶対イプシロンで通常直面する問題) のいずれであっても問題ありません。

短所:

  • 数値を「丸める」精度を制御しません。私のシステムの Fe は、数値の 10 進数表現で最初の有効な (ゼロ以外の) 1 の後の 6 桁です (ほとんどの場合、これで十分です)。
于 2020-09-10T21:56:31.947 に答える
-4

ビット単位の XOR を実行しないのはなぜですか? 対応するビットが等しい場合、2 つの浮動小数点数は等しくなります。指数ビットを仮数の前に配置するという決定は、2 つの float の比較を高速化するために行われたと思います。ここでの多くの回答には、イプシロン比較のポイントが欠けていると思います。イプシロン値は、浮動小数点数が比較される精度にのみ依存します。たとえば、float を使用していくつかの演算を行った後、2.5642943554342 と 2.5642943554345 の 2 つの数値が得られます。それらは等しくありませんが、ソリューションでは 10 進数の 3 桁のみが問題になるため、2.564 と 2.564 で等しくなります。この場合、0.001 に等しいイプシロンを選択します。イプシロンの比較は、ビットごとの XOR でも可能です。私が間違っている場合は修正してください。

于 2016-11-13T21:42:21.030 に答える