浮動小数点と二重比較の最も効果的な方法は何ですか?


524

2つdoubleまたは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);
}

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

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


2
>関数の最初に...を追加する方が効率的ですか?<invoke Knuth>時期尚早の最適化は、すべての悪の根源です。</invoke Knuth>上記のabs(ab)<EPSを使用するだけで、明確で理解しやすくなります。
Andrew Coleson 2008


2
元のポスターの実装に関して最適でない唯一のものは、&&に追加のブランチが含まれていることです。OJの答えは最適です。fabsはx87の単一の命令である組み込み関数であり、他のほとんどすべてのものを想定しています。OJの回答を受け入れます!
3yE

3
可能であれば、浮動小数点をドロップし、固定小数点を使用します。たとえば、{floating point}メートルの代わりに{fixed point}ミリメートルを使用します。
Thomas Matthews

33
「これを行うだけでは正しくありません」 -これは単なるごみです。もちろん、使用==は完全に正しい可能性がありますが、これは質問に示されていないコンテキストに完全に依存します。そのコンテキストがわかるまで==は、「最も効率的な方法」のままです。
クリスチャンラウ

回答:


459

他の提案を使用する場合は、十分に注意してください。それはすべてコンテキストに依存します。

私は推定システムのバグトレースに長い時間を費やしているa==b場合を|a-b|<epsilon。根本的な問題は次のとおりです。

  1. このアルゴリズムでは、暗黙の前提いる場合a==bb==c、その後a==c

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

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

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

同様に私は言った:それはすべてのコンテキストとの予想サイズに依存aしてb

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

また、基本的にint算術演算doublesがある場合(ここではdoubleを使用してint値を保持する場合があります)、演算は正しく行われます。たとえば、4.0 / 2.0は1.0 + 1.0と同じです。これは、結果が小数(4.0 / 3.0)になることを行わないか、intのサイズの外に出ない限りです。


10
明白なものを指摘するための+1(それはしばしば無視されます)。ジェネリックメソッドの場合、イプシロンを相対的に作成できますがfabs(a)+fabs(b)、NaN、0和、オーバーフローを補償すると、これは非常に複雑になります。
peterchen 2010

4
わからないことがあるはずです。典型的なfloat/ doubleMANTISSA x 2 ^ EXPです。 epsilon指数に依存します。たとえば、仮数が24 ビットで指数が8ビットの符号付きの場合、1/(2^24)*2^127または~2^103epsilon一部の値のです。またはこれは最小イプシロンを指しているのですか?
アートレスノイズ2013

3
一瞬待って。私が言ったことはあなたが意味したことですか?あなたは、理由|a-b|<epsilonが正しくないと言ってます。このリンクを回答に追加してください。cygnus-software.com/papers/comparingfloats/comparingfloats.htmに同意し、私は私の愚かなコメントを削除することができます。
アートレスノイズ2013

3
これは非常に長いコメントであり、それ自体は回答ではありません。すべてのコンテキストに(一連の)正解はありますか?
Merlyn Morgan-Graham

2
古いリンクは、新しいページはこちら時代遅れのようですrandomascii.wordpress.com/2012/02/25/...
Marson真央

174

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

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

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

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


@OJ:最初のコードサンプルに何か問題がありますか?唯一の問題は次のような状況にあると思いました。float a = 3.4; if(a == 3.4){...}つまり、格納された浮動小数点をリテラルと比較している場合です。この場合、両方の数値が格納されているので、等しい場合、それらは同じ表現を持ちa == bます。
Lazer、2010

11
@DonReba:EPSILONがとして定義されてDBL_EPSILONいる場合のみ。通常、必要な比較の精度に応じて選択される特定の値になります。
Nemo157 2011

7
EPSILON連続するフロート間の差も大きくなるため、フロートが大きい場合、比較は機能しません。この記事を参照してください
kevintodisco 2013年

22
バトルフィールド4のように、テクスチャ/オブジェクトが遠くにちらつく場合、一部のゲームでZファイティングが発生するのも不思議ではありませんEPSILON。手元のユニットにとって意味のあるしきい値と比較する必要があります。また、std::absさまざまな浮動小数点型に対してオーバーロードされるため、使用してください。
Maxim Egorushkin 2014

11
サンプルコードは、大多数のプログラマーによって繰り返される典型的なバグを示しているため、私は反対票を投じました。浮動小数点は(固定小数点ではなく)浮動小数点であるため、常に相対誤差が原因です。そのため、修正されたエラー(epsilon)で正しく機能することはありません。
user2261015

115

私は、ことがわかったGoogleのC ++テストフレームワークは、ダブル、フロートの両方で動作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を追加し、上部にライセンスを追加しました。

#define GTEST_OS_WINDOWSをある値に設定してください(または、コードが使用されているコードをコードベースに適合するものに変更してください-結局、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のソースコードから最新バージョンを直接入手するのが最善です。


3
+1:私はこれが正しいことに同意します。ただし、その理由は説明されていません。ここを参照してください:cygnus-software.com/papers/comparingfloats/comparingfloats.htmここ のトップスコアに関するコメントを書き込んだ後、このブログ投稿を読みました。私はそれが同じことを言い、上で実装された合理的/解決策を提供すると信じています。コードがたくさんあるので、人々は答えを見逃します。
ノイズ

たとえばFloatPoint <double> fp(0.03f)を実行すると、暗黙のキャストが発生したときに発生する可能性のある厄介なことがいくつかあります。これを防ぐために、これにいくつかの変更を加えました。template <typename U>明示的なFloatingPoint(const U&x){if(typeid(U).name()!= typeid(RawType).name()){std :: cerr << "あなたは暗黙の変換をFloatingPoint、Do n't "<< std :: endl; assert(typeid(U).name()== typeid(RawType).name()); } u_.value_ = x; }
JeffCharter 2014年

2
良い発見!ただし、このコードが盗まれた場所をGoogle Testに投稿するのが最善だと思います。新しいバージョンがある可能性があることを反映するために、投稿を更新します。グーグルの人たちがかゆい行動をとるなら、それを例えばGitHub要旨に入れてもらえますか?それから私もそれにリンクします。
skrebbel 2014年

3
最新のコードスニペットについては、こちらこちらをご覧ください。
Jaege、2016年

1
必要な行を要旨ファイルに抽出しました。ここからどなたでもアクセスできます
YusufTarıkGünaydın2017

111

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

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);
}

もちろん、イプシロンの選択は状況によって異なり、数値をどの程度等しくするかを決定します。

浮動小数点数を比較する別の方法は、数値のULP(最後の場所の単位)を調べることです。特に比較については扱っていませんが、浮動小数点数についてすべてのコンピューターサイエンティストが知っておくべき論文は、ULPの概要など、浮動小数点の仕組みと落とし穴を理解するための優れたリソースです。


1
小さい/大きい数を判別する方法を投稿していただきありがとうございます!
トマト

1
fabs(a - b) <= ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);私の命を救った。:LOLまた、浮動小数点数(例の不可欠な部分で発生する可能性のある変更を考慮し、このバージョン(私の場合はチェックしていないが、あまりにも他人のために適用される)ことに注意してください2147352577.9999997616 == 2147352576.0000000000どこが明確にほとんど差があることがわかります22つの数値の間)は、これは非常に良いことです。これは、累積された丸め誤差が数値の小数部をオーバーフローしたときに発生します。
rbaleksandar

ブルース・ドーソンによる非常に素晴らしくて役に立つ記事、ありがとう!
BobMorane 2018

2
この質問にC ++のタグが付けられていると、チェックはstd::max(std::abs(a), std::abs(b))(またはstd::min())として記述されているので読みやすくなります。std::absC ++では、float型とdouble型でオーバーロードされているため、問題なく機能します(fabsただし、常に読みやすくするために維持できます)。
ラザケル

1
問題は私のコードにあったことが判明しました。元の期待値と解析された文字列の違いです。
mwpowellhtx

47

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

// 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;    
}

14
maxUlpsの推奨値は何ですか?
unj2 2011

6
*(int*)&A;」は厳密なエイリアスルールに違反しますか?
osgx 2011

3
gtest(ULPの検索)によると、4が許容数です。
May Oakes 2012

4
そして、ここでブルース・ドーソンの論文(の1紙のイントロにリンクされている)に結合の更新は、以下のとおりです。randomascii.wordpress.com/2012/02/25/...randomascii.wordpress.com/2012/06/26/...
マイケルバー

3
ULPが何であるかを理解する
のに少し時間がかかり

27

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

浮動小数点の許容誤差の再検討でやや実用的な記事を見つけることができ、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 ;
}

25

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();
}

34
ほとんどの場合、そのイプシロンの倍数が必要になります。
user7116 2010

11
std :: absを使用できませんか?AFAIK、std :: absはdoubleでもオーバーロードされます。私が間違っている場合は警告してください。
kolistivra

3
@kolistivra、あなたは間違っています。「fabs」の「f」は、float型を意味しません。おそらくC関数fabsf()とfabsl()を考えているでしょう。
jcoffland 2012年

9
実際、Bruceの記事で概説され ている理由によりイプシロンは浮動小数点値が大きくなるにつれて変化します。彼が「2.0より大きい数値の場合、フロート間のギャップは大きくなり、FLT_EPSILONを使用してフロートを比較する場合、より高価で明白でない等価性チェックを行っているだけだ」
bobobobo 2013年

5
私はこれが古いことを知っていますが、cmathの浮動小数点型ではstd :: absがオーバーロードされています。
mholzmann 2013年

18

私は結局、この素晴らしいスレッドの資料を調べるのにかなりの時間を費やしてしまいました。私は誰もが多くの時間を費やしたいと思っているので、私が学んだことの要約と私が実装したソリューションを強調します。

簡単なまとめ

  1. 1e-8は1e-16とほぼ同じですか?ノイズの多いセンサーデータを表示している場合は、おそらくはいですが、分子シミュレーションを実行している場合は、そうでない可能性があります。結論:常に特定の関数呼び出しのコンテキストで許容値を考える必要があり、それを単なるアプリ全体のハードコードされた定数にするだけではありません。
  2. 一般的なライブラリ関数の場合、デフォルトの許容値を持つパラメーターがあると便利です。典型的な選択はnumeric_limits::epsilon()、float.hのFLT_EPSILONと同じです。ただし、1.0のような値を比較するためのイプシロンは1E9のような値のためのイプシロンと同じではないため、これには問題があります。FLT_EPSILONは1.0に対して定義されています。
  3. 数値が許容範囲内にあるかどうかを確認する明らかな実装はfabs(a-b) <= epsilon、デフォルトのイプシロンが1.0に定義されているため機能しません。イプシロンをaとbに関して拡大または縮小する必要があります。
  4. この問題には2つの解決策があります。イプシロンを比例的に設定するmax(a,b)か、aの周りに次の表現可能な数値を取得してから、bがその範囲内にあるかどうかを確認します。前者は「相対」法と呼ばれ、後にULP法と呼ばれます。
  5. どちらの方法も、0と比較すると実際には失敗します。この場合、アプリケーションは正しい許容誤差を提供する必要があります。

ユーティリティ関数の実装(C ++ 11)

//implements relative method - do not use for comparing with zero
//use this most of the time, tolerance needs to be meaningful in your context
template<typename TReal>
static bool isApproximatelyEqual(TReal a, TReal b, TReal tolerance = std::numeric_limits<TReal>::epsilon())
{
    TReal diff = std::fabs(a - b);
    if (diff <= tolerance)
        return true;

    if (diff < std::fmax(std::fabs(a), std::fabs(b)) * tolerance)
        return true;

    return false;
}

//supply tolerance that is meaningful in your context
//for example, default tolerance may not work if you are comparing double with float
template<typename TReal>
static bool isApproximatelyZero(TReal a, TReal tolerance = std::numeric_limits<TReal>::epsilon())
{
    if (std::fabs(a) <= tolerance)
        return true;
    return false;
}


//use this when you want to be on safe side
//for example, don't start rover unless signal is above 1
template<typename TReal>
static bool isDefinitelyLessThan(TReal a, TReal b, TReal tolerance = std::numeric_limits<TReal>::epsilon())
{
    TReal diff = a - b;
    if (diff < tolerance)
        return true;

    if (diff < std::fmax(std::fabs(a), std::fabs(b)) * tolerance)
        return true;

    return false;
}
template<typename TReal>
static bool isDefinitelyGreaterThan(TReal a, TReal b, TReal tolerance = std::numeric_limits<TReal>::epsilon())
{
    TReal diff = a - b;
    if (diff > tolerance)
        return true;

    if (diff > std::fmax(std::fabs(a), std::fabs(b)) * tolerance)
        return true;

    return false;
}

//implements ULP method
//use this when you are only concerned about floating point precision issue
//for example, if you want to see if a is 1.0 by checking if its within
//10 closest representable floating point numbers around 1.0.
template<typename TReal>
static bool isWithinPrecisionInterval(TReal a, TReal b, unsigned int interval_size = 1)
{
    TReal min_a = a - (a - std::nextafter(a, std::numeric_limits<TReal>::lowest())) * interval_size;
    TReal max_a = a + (std::nextafter(a, std::numeric_limits<TReal>::max()) - a) * interval_size;

    return min_a <= b && max_a >= b;
}

isDefinitelyLessThanチェックdiff < tolerance、これはaとbがほぼ等しいことを意味します(したがって、aは確実にbよりも小さくありません)。両方のケースでdiff>許容値をチェックする方が理にかなっていますか?またはorEqualTo、おおよその等価性チェックがtrueを返すかどうかを制御する引数を追加することもできます。
Matt Chambers

14

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

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

正しいコードは次のようになります:

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

(そして、はい、これは異なります)

場合によっては、ファブによって怠惰な評価が失われることはないのではないでしょうか。コンパイラ次第だと思います。両方試してみてください。それらが平均して同等である場合、ファブでの実装を検討してください。

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

最後に、この関数をインライン化すると、より良い結果が得られる可能性があります。しかし、あまり改善しないと思います...

編集:OJ、コードを修正してくれてありがとう。それに応じてコメントを消去しました


13

`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十分だとわかった場合は、完璧です-使用してください!しかし、その欠点と他の可能な解決策にも注意してください。


3
タイプミス(s /-/、/ fmax()にコンマがない)を除いて、この実装にはEPSILON内にあるゼロに近い数値のバグがありますが、まだ完全にはバグがありません。たとえば、AreSame(1.0E-10、1.0E-9)は、相対エラーが大きいためfalseを報告します。あなたはあなたの会社のヒーローになります。
brlcad

1
@brlcad 浮動小数点のポイントを取得できませんでした。1.0E-10と1.0E-9は10の大きさだけ異なります。したがって、同じではないことは事実です。浮動小数点は常に相対誤差です。1.0E-10と1.0E-9をほぼ等しいと見なすシステムがある場合、どちらも「かなりゼロに近い」ため(人間には合理的に聞こえますが、数学的には何もありません)、そのEPSILONを適切に調整する必要があります。そのようなシステムのために。
user2261015 2015年

8

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

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

したがって、0.00001などの固定小数点数と浮動小数点値(指数は任意)を混ぜ合わせても意味がありません。これは、オペランド値が狭いドメイン内にある(つまり、特定の指数に近い)ことが確実で、その特定のテストのイプシロン値を適切に選択した場合にのみ機能します。数値を空中から引き出した場合(「Hey!0.00001は小さいので、それで問題ないはずです!」)、数値エラーに運命づけられます。私は多くの時間を費やして、悪い数値コードをデバッグして、いくつかの悪いシュマックがランダムなイプシロン値を投げて、さらに別のテストケースを機能させました。

あなたはどのような種類の数値プログラミングを行うと、あなたは固定小数点イプシロンのために到達する必要があると思われる場合は、READ BRUCEの記事ONは、浮動小数点数を比較します

浮動小数点数の比較


5

Qtは2つの関数を実装しています。おそらくそれらから学ぶことができます。

static inline bool qFuzzyCompare(double p1, double p2)
{
    return (qAbs(p1 - p2) <= 0.000000000001 * qMin(qAbs(p1), qAbs(p2)));
}

static inline bool qFuzzyCompare(float p1, float p2)
{
    return (qAbs(p1 - p2) <= 0.00001f * qMin(qAbs(p1), qAbs(p2)));
}

そして、次の機能が必要になる場合があります。

p1またはp2のいずれかが0.0である値の比較は機能せず、値の1つがNaNまたは無限大である値の比較も機能しないことに注意してください。いずれかの値が常に0.0の場合は、代わりにqFuzzyIsNullを使用してください。値の1つが0.0である可能性が高い場合、1つの解決策は両方の値に1.0を追加することです。

static inline bool qFuzzyIsNull(double d)
{
    return qAbs(d) <= 0.000000000001;
}

static inline bool qFuzzyIsNull(float f)
{
    return qAbs(f) <= 0.00001f;
}

3

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


3

残念ながら、「無駄な」コードでさえ正しくありません。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)))
{
    // ...
}

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


2
「EPSILONは、1.0に追加してその値を変更できる最小の値です」:実際、この栄誉は0.5 * EPSILONの後継(デフォルトの丸め直近モード)に適用されます。blog.frama-c.com/index.php?post/2013/05/09/FLT_EPSILON
Pascal Cuoq 2013

なぜあなたEPSILONは質問の中にあると思いますDBL_EPSILONFLT_EPSILON?問題はあなた自身の想像力にあり、DBL_EPSILONそれを使用しないコードに(実際には間違った選択になります)置き換えました。
Ben Voigt 2015

@BenVoigt、その通りです、それは当時私の心の中にあったものであり、私はその観点から質問を解釈しました。
Don Reba 2015

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 );
                  }
};

2

これが使用の証明です std::numeric_limits::epsilon()が答えではないというがあります。1より大きい値では失敗します。

上記の私のコメントの証明:

#include <stdio.h>
#include <limits>

double ItoD (__int64 x) {
    // Return double from 64-bit hexadecimal representation.
    return *(reinterpret_cast<double*>(&x));
}

void test (__int64 ai, __int64 bi) {
    double a = ItoD(ai), b = ItoD(bi);
    bool close = std::fabs(a-b) < std::numeric_limits<double>::epsilon();
    printf ("%.16f and %.16f %s close.\n", a, b, close ? "are " : "are not");
}

int main()
{
    test (0x3fe0000000000000L,
          0x3fe0000000000001L);

    test (0x3ff0000000000000L,
          0x3ff0000000000001L);
}

実行すると、次の出力が生成されます。

0.5000000000000000 and 0.5000000000000001 are  close.
1.0000000000000000 and 1.0000000000000002 are not close.

2番目のケース(1つで1より大きい)の場合、2つの入力値は可能な限り接近しているが、まだ接近していないと比較していることに注意してください。したがって、1.0より大きい値の場合、同等性テストを使用することもできます。固定されたイプシロンは、浮動小数点値を比較するときに保存されません。


私は信じてreturn *(reinterpret_cast<double*>(&x));それが通常動作しますが、実際に未定義の動作です。
Jaap Versteegh

公正なポイント、ただしこのコードは例示でnumeric_limits<>::epsilonあり、IEEE 754フローリングポイントの問題を示すのに十分です。
Steve Hollasch

また、公平なポイントですが、そのような洞察を期待してスタックオーバーフローに投稿するのは賢明ではありません。コード盲目的にコピーされるため、この非常に一般的なパターンを(ユニオントリックと共に)根絶することがさらに困難になり、すべてのUDと同様に回避する必要があります。
Jaap Versteegh

1

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";
}

0

私は、浮動小数点減算を含むこれらの答え(たとえば、fabs(ab)<epsilon)に非常に注意します。まず、浮動小数点数は、マグニチュードが大きく、間隔がイプシロンよりも大きいマグニチュードで十分にスパースになると、a == bを実行するだけで済みます。第2に、2つの非常に近い浮動小数点数を減算すること(これは、ほぼ等しいことを探している場合、傾向があるため)は、破壊的なキャンセルを得る方法です。

移植性はありませんが、グロムの答えはこれらの問題を回避するための最良の仕事をすると思います。


1
良い情報のための+1。ただし、相対誤差を大きくすることで、等価比較を台無しにする方法はわかりません。IMHOエラーは、減算の結果でのみ重要になりますが、減算される2つのオペランドのそれと比較した桁は、同等性を判断するのに十分信頼できるはずです。全体の解像度を高くする必要がある場合を除き、その場合の唯一の解決策は、仮数部に上位ビットを持つ浮動小数点表現に移動することです。
sehe

ほぼ等しい2つの数値を減算しても、致命的なキャンセルにはなりません-実際には、まったくエラーが発生しません(qv Sterbenzの定理)。aand b自身の計算中に、壊滅的なキャンセルが発生します。ファジー比較の一部として、浮動小数点減算を使用しても問題全く存在しない(他の人が言っているようにかかわらず、絶対イプシロン値または所与の用途の場合に適切であってもなくてもよいが。)
Sneftel

0

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

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

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


科学的な計算や数値解析のバックグラウンド(つまりLAPACK、BLAS)を持たない人が完全性を理解できないように制限することは非常に専門的ですが、実際には良い答えへの非常に確かな参照です。または、言い換えると、数値レシピの紹介や負担&フェアによる数値分析などを読んだことが前提となっています。
mctylr 2013

0

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

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

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

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


0

量のスケールに関して:

場合はepsilon、いくつかの特定の物理的な意味での量の大きさのごく一部(すなわち相対値)であるとAし、B種類は、私は以下のが非常に正確であること、考えているよりも、同じ意味で同等です:

#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;
}

0

私はこのコードを使用します:

bool AlmostEqual(double v1, double v2)
    {
        return (std::fabs(v1 - v2) < std::fabs(std::min(v1, v2)) * std::numeric_limits<double>::epsilon());
    }

2
それepsilonが目的ではありません。
Sneftel

1
何故なの?説明できますか?
デビュー2008

2
@debuti epsilonは、1と1の後の次の表現可能な数値の間の距離にすぎません。せいぜい、そのコードは2つの数値が互いに完全等しいかどうかを確認しようとしているだけですが、2のべき乗ではないものが乗算されるepsilonため、それも正しく行っていません。
Sneftel

2
ああ、std::fabs(std::min(v1, v2))不正解です。負の入力の場合は、大きさが大きい方を選択します。
Sneftel

0

私はこれをJavaのために書いていますが、多分あなたはそれが便利だと思います。doubleではなくlongを使用しますが、NaN、非正規などを処理します。

public static boolean equal(double a, double b) {
    final long fm = 0xFFFFFFFFFFFFFL;       // fraction mask
    final long sm = 0x8000000000000000L;    // sign mask
    final long cm = 0x8000000000000L;       // most significant decimal bit mask
    long c = Double.doubleToLongBits(a), d = Double.doubleToLongBits(b);        
    int ea = (int) (c >> 52 & 2047), eb = (int) (d >> 52 & 2047);
    if (ea == 2047 && (c & fm) != 0 || eb == 2047 && (d & fm) != 0) return false;   // NaN 
    if (c == d) return true;                            // identical - fast check
    if (ea == 0 && eb == 0) return true;                // ±0 or subnormals
    if ((c & sm) != (d & sm)) return false;             // different signs
    if (abs(ea - eb) > 1) return false;                 // b > 2*a or a > 2*b
    d <<= 12; c <<= 12;
    if (ea < eb) c = c >> 1 | sm;
    else if (ea > eb) d = d >> 1 | sm;
    c -= d;
    return c < 65536 && c > -65536;     // don't use abs(), because:
    // There is a posibility c=0x8000000000000000 which cannot be converted to positive
}
public static boolean zero(double a) { return (Double.doubleToLongBits(a) >> 52 & 2047) < 3; }

いくつかの浮動小数点演算を行った後、数値は予想と大きく異なる場合があることに注意してください。それを修正するコードはありません。


0

これはどう?

template<typename T>
bool FloatingPointEqual( T a, T b ) { return !(a < b) && !(b < a); }

私はさまざまなアプローチを見てきましたが、これを見たことがないので、コメントも聞きたいです。


これは1.99999999および1.99999998では機能しません
Mehdi

@Mehdi repl.it/repls/SvelteSimpleNumerator#main.cppを試したところ、期待どおりに動作するように見えますが、これを行わない特定のコンパイラ実装を参照しているのではないでしょうか。
derke

-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.0000009xxxです。1.0と1.0000011でテストしましたが、今回は期待した結果が得られます。


-1

これはラムダを使用した別のソリューションです:

#include <cmath>
#include <limits>

auto Compare = [](float a, float b, float epsilon = std::numeric_limits<float>::epsilon()){ return (std::fabs(a - b) <= epsilon); };

これは、ラムダであり、説明がないことを除いて、他の多くの回答とまったく同じです。そのため、これは回答としてあまり価値がありません。
stijn

-2

私のやり方は正しくないかもしれませんが、役に立つかもしれません

両方の浮動小数点数を文字列に変換してから、文字列を比較します

bool IsFlaotEqual(float a, float b, int decimal)
{
    TCHAR form[50] = _T("");
    _stprintf(form, _T("%%.%df"), decimal);


    TCHAR a1[30] = _T(""), a2[30] = _T("");
    _stprintf(a1, form, a);
    _stprintf(a2, form, b);

    if( _tcscmp(a1, a2) == 0 )
        return true;

    return false;

}

オペレーターのオーバーロードも行うことができます


+1:ねえ、私はこれでゲームプログラミングをするつもりはありませんが、問題についてブルースドーソンのブログ(論文?:D)で、フロートを往復するというアイデアが何度か出てきました。部屋と誰かがあなたの頭に銃を置いて、「ねえ、2つのフロートをXの有効数字の範囲内と比較しなければならない。5分だよ、GO!」これはおそらく考慮すべきものです。;)
shelleybutterfly 2014年

@shelleybutterfly次に、問題は2つの浮動小数点数を比較する最も効率的な方法です。
トミーアンデルセン

@TommyA LOLおそらく、効率性に関連しない理由でラウンドトリップが反対票を投じられたに違いない。私の直感は、HW fp数学と比較してかなり非効率的であるということですが、ソフトウェアfpのアルゴリズムには少なくともbigOの差があるとは考えにくいとも述べています。その場合の効率の懸念が重要であることを示すあなたの分析を心待ちにしています。その上、時々最適ではないことは依然として価値のある答えになる可能性があり、それは反対票が投じられたため、ドーソンのブログで話題についてさえ言及された有効な手法であるにもかかわらず、賛成に値するものだと思いました。
shelleybutterfly 2014

-2

2つdoubleを固定と比較することはできませんEPSILON。値に応じてdoubleEPSILON変化します。

より良い二重比較は:

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;
}

-2

より一般的な方法で:

template <typename T>
bool compareNumber(const T& a, const T& b) {
    return std::abs(a - b) < std::numeric_limits<T>::epsilon();
}

4
数字場合のように、この方法では、多くの弱点を持っているaとは、bより既に小さいですepsilon()が違い、まだ重要となり得ます。逆に、数値が非常に大きい場合は、数ビットのエラーであっても、数値を等しいと見なしたい場合でも、比較が失敗します。この答えは、まさに避けたいタイプの「汎用」比較アルゴリズムです。
SirGuy 2016

-3

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


複数の質問に同じ回答を追加しないでください。最良のものに答え、残りを重複としてフラグを立てます。meta.stackexchange.com/questions/104227/…を
Bhargav Rao

同じ形式の正規化された表現に制限されていても、ExOr(および1つまたは2つの比較)だけを使用して「イプシロン比較」が可能だとは思わない
greybeard 2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.