부동 및 이중 비교에 가장 효과적인 방법은 무엇입니까?


524

두 개 double또는 두 개의 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로 가면 명확하고 이해하기 쉽습니다.
앤드류 콜슨


2
오리지널 포스터의 구현에 대해 최적이 아닌 유일한 것은 &&에 추가 브랜치를 포함한다는 것입니다. OJ의 답변이 최적입니다. fabs는 x87에 대한 단일 명령 인 내장형이며 거의 다른 것도 가정합니다. OJ의 답변을 이미 수락하십시오!
3yE

3
가능하면 부동 소수점을 삭제하고 고정 소수점을 사용하십시오. 예를 들어, {floating point} 미터 대신 {fixed point} 밀리미터를 사용하십시오.
Thomas Matthews

33
"단순히 정확하지 않다" -이것은 단지 쓰레기 일 뿐이다. 물론 사용하는 ==것은 완벽하게 정확할 수 있지만, 이것은 전적으로 질문에 제시되지 않은 맥락에 달려있다. 해당 컨텍스트가 알려질 때까지 "가장 효율적인 방법"을== 유지합니다 .
Christian Rau

답변:


459

다른 제안을 사용하면 매우주의하십시오. 그것은 모두 상황에 달려 있습니다.

나는 a==bif로 추정되는 시스템의 버그를 추적하는 데 오랜 시간을 보냈습니다 |a-b|<epsilon. 근본적인 문제는 다음과 같습니다.

  1. 알고리즘의 암시 적 가정은 다음 a==b과 같습니다 .b==ca==c

  2. 인치 단위로 측정 된 라인과 밀 단위 (.001 인치)로 측정 된 라인에 동일한 엡실론을 사용합니다. 즉 a==b하지만 1000a!=1000b. (AlmostEqual2sComplement가 엡실론 또는 최대 ULPS를 요구하는 이유입니다).

  3. 각도의 코사인과 선 길이 모두에 동일한 엡실론을 사용합니다!

  4. 이러한 비교 기능을 사용하여 컬렉션의 항목을 정렬합니다. (이 경우 내장 C ++ 연산자 ==를 두 배로 사용하면 올바른 결과가 나타납니다.)

마찬가지로 나는 말했다 : 그것은 모든 상황과의 예상 크기에 따라 ab.

BTW 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

4
내가 이해하지 못하는 것이 있어야합니다. 일반적인 float/ doubleMANTISSA x 2 ^ EXP 입니다. epsilon지수에 따라 달라집니다. 예를 들어, 만약 가수가 24 비트이며, 지수가 8 비트를 체결 후 1/(2^24)*2^127또는 ~2^103이다 epsilon일부 값은; 또는 이것은 최소 엡실론을 의미 합니까?
artless noise

3
잠시만 요. 내가 무슨 말을했는지 당신은 이유 |a-b|<epsilon올바르지 않습니다 . 답변에이 링크를 추가하십시오. cygnus-software.com/papers/comparingfloats/comparingfloats.htm에 동의하면 내 멍청한 의견을 제거 할 수 있습니다.
artless noise

3
이것은 그 자체로는 답이 아니라 매우 긴 주석입니다. 모든 상황에 대해 정식 답변이 있습니까?
Merlyn Morgan-Graham

2
이전 링크는 더 이상 사용되지 않는 것 같습니다. 새 페이지는 다음과 같습니다. randomascii.wordpress.com/2012/02/25/…
Marson Mao

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

11
@DonReba : EPSILON로 정의 된 경우에만 DBL_EPSILON. 일반적으로 필요한 비교 정확도에 따라 선택된 특정 값이됩니다.
Nemo157

7
EPSILON플로트가 큰 경우 연속 플로트 간의 차이도 커지므로 비교가 작동하지 않습니다. 이 기사를 참조 하십시오 .
kevintodisco

22
Battlefield 4에서와 같이 텍스처 / 오브젝트가 멀리 깜빡 일 때 일부 게임에서 Z- 파이팅이 있다는 것은 놀라운 일이 아닙니다 EPSILON. 해당 장치에 적합한 임계 값과 비교해야합니다. 또한 std::abs부동 소수점 유형에 따라 오버로드되기 때문에 사용 하십시오.
Maxim Egorushkin

11
예제 코드는 일반적인 버그 whis가 대다수의 프로그래머에 의해 반복됨을 보여주기 때문에 하향식되었습니다. 부동 소수점은 고정 소수점이 아닌 부동 소수점이므로 항상 상대 오류에 관한 것입니다. 따라서 고정 오류 (epsilon)로 올바르게 작동하지 않습니다.
user2261015

115

나는 것을 발견 구글 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 를 추가하고 라이센스를 맨 위에 추가했습니다.

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 테스트 소스 코드에서 최신 버전을 다운로드 하십시오.


3
+1 :이 말이 맞다는 데 동의합니다. 그러나 이유를 설명하지는 않습니다. 여기를 참조하십시오 : cygnus-software.com/papers/comparingfloats/comparingfloats.htm 나는 최고 점수에 대한 내 의견을 쓴 후이 블로그 게시물을 읽었습니다. 나는 그것이 똑같은 말을하고 위에서 구현 된 합리적인 / 솔루션을 제공한다고 생각합니다. 코드가 너무 많기 때문에 사람들은 답을 놓칠 것입니다.
artless noise

FloatPoint <double> fp (0.03f)라고 말하면 암시 적 캐스트가 발생할 때 발생할 수있는 불쾌한 일이 몇 가지 있습니다. 이를 방지하기 위해 몇 가지 사항을 수정했습니다. template <typename U> 명시 적 FloatingPoint (const U & x) {if (typeid (U) .name ()! = typeid (RawType) .name ()) {std :: cerr << "암시 적으로 변환하고 있습니다 FloatingPoint,하지 말 것 "<< std :: endl; assert (typeid (U) .name () == typeid (RawType) .name ()); } u_.value_ = x; }
JeffCharter

2
잘 찾아라! 그러나이 코드를 도난당한 Google 테스트에 기여하는 것이 가장 좋습니다. 아마도 최신 버전이있을 수 있도록 게시물을 업데이트하겠습니다. Google 직원이 가려운 행동을하는 경우 GitHub 요점에 넣을 수 있습니까? 그런 다음 연결합니다.
skrebbel

3
최신 코드 스 니펫은 herehere을 참조 하십시오 .
Jaege

1
요점 파일에 필요한 줄을 추출했습니다. 누구나 여기 에서 연락 할 수 있습니다 .
Yusuf Tarık Günaydın

111

부동 소수점 숫자를 비교하는 것은 컨텍스트에 따라 다릅니다. 연산 순서를 변경하더라도 다른 결과가 나올 수 있기 때문에 숫자가 얼마나 "동일"해야하는지 아는 것이 중요합니다.

부동 소수점 숫자를 비교 브루스 도슨은 부동 소수점 비교에서 볼 때 시작하기 좋은 장소입니다.

다음은 Knuth의 컴퓨터 프로그래밍 기술에 대한 정의입니다 .

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거의 차이가 있음을 분명히 알 수 있습니다) 2두 숫자 사이)가 꽤 좋습니다! 누적 된 반올림 오류가 숫자의 소수 부분을 오버플로 할 때 발생합니다.
rbaleksandar

Bruce Dawson의 매우 훌륭하고 유용한 기사, 감사합니다!
BobMorane

2
이 질문에 C ++ 태그가 붙어 있다고 가정하면, 수표를 std::max(std::abs(a), std::abs(b))(또는로 std::min()) 작성하는 것이 더 쉽습니다 . std::absC ++에서는 float 및 double 유형으로 오버로드되므로 잘 작동합니다 (항상 fabs가독성을 유지할 수 있음 ).
Razakhel

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

6
" *(int*)&A;"가 엄격한 앨리어싱 규칙을 위반합니까?
osgx

3
gtest (ULP 검색)에 따르면 4는 허용 가능한 숫자입니다.
May Oakes

4
다음은 Bruce Dawson의 논문에 대한 몇 가지 업데이트입니다 ( randomascii.wordpress.com/2012/02/25/…randomascii.wordpress.com/2012/06/26/…
Michael Burr

3
ULP가 무엇인지 알아내는 데 시간이 조금 걸렸습니다. 마지막 장소의 유닛
JeffCharter

27

이것이 오래된 스레드라는 것을 알고 있지만이 기사는 부동 소수점 숫자를 비교할 때 찾은 가장 간단한 기사 중 하나이며 더 자세히 알아 보려면 더 자세한 참조 자료가 있으며 기본 사이트는 전체 범위의 문제를 다룹니다. 부동 소수점 숫자 처리 부동 소수점 안내서 : 비교 .

우리는에서 좀 더 실제적인 기사 찾을 수 있습니다 부동 소수점 재 방문 허용 오차 가 메모를 절대 허용 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 ;
}

이 기사에서는 절대 테스트가 실패 할 때 x와 실패 할 때 y실패하고 상대 사례가 작을 때 실패한다고 지적합니다 . 그가 절대 및 상대 공차가 같다고 가정하면 결합 테스트는 다음과 같습니다.

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

11
std :: abs를 사용할 수 없습니까? AFAIK, std :: abs도 두 배로 오버로드됩니다. 내가 틀렸다면 경고하십시오.
kolistivra

3
@kolistivra, 당신은 틀렸다. 'fabs'의 'f'는 플로트 유형을 의미하지 않습니다. 아마도 C 함수 fabsf ()와 fabsl ()을 생각하고있을 것입니다.
jcoffland

9
실제로 Bruce의 기사 epsilon에 설명 된 이유로 부동 소수점 값이 커질수록 변경 됩니다. 그가 말하는 부분을 참조하십시오 "2.0보다 큰 번호는 수레 사이의 간격이 더 큰 성장하고 당신이 당신이 단지 더 비싼 덜 명백한 평등 점검을하고 다음 FLT_EPSILON를 사용하여 수레를 비교한다면."
bobobobo

5
나는 이것이 오래되었지만 std :: abs가 cmath의 부동 소수점 유형에 과부하가 걸린다는 것을 알고 있습니다.
mholzmann 2016 년

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그러나 숫자가 허용 오차 내에 있는지 확인하는 확실한 구현은 기본 epsilon이 1.0에 대해 정의되어 있기 때문에 작동하지 않습니다. 우리는 a와 b의 관점에서 엡실론을 확대 또는 축소해야합니다.
  4. 이 문제에 대한 두 가지 해결책이 있습니다. epsilon을 비례로 설정 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;
}

isDefinitelyLessThanchecks diff < tolerance는 a와 b가 거의 같음을 의미합니다 (따라서 a는 b보다 작지 않습니다). 두 경우 모두 diff> 공차를 확인하는 것이 더 합리적이지 않습니까? 또는 orEqualTo근사 동등성 검사가 true를 반환해야하는지 여부를 제어 하는 인수를 추가 하십시오.
Matt Chambers

14

작성한 코드는 버그가 있습니다.

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

올바른 코드는 다음과 같습니다.

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

(... 그렇습니다.)

어떤 경우에는 팹이 게으른 평가를 잃지 않을지 궁금합니다. 컴파일러에 따라 다릅니다. 둘 다 시도해 볼 수 있습니다. 그것들이 평균과 같으면 팹으로 구현하십시오.

두 플로트 중 어떤 플로트가 다른 플로트보다 더 큰지에 대한 정보가 있다면, 지연 순서를보다 잘 활용하기 위해 비교 순서대로 재생할 수 있습니다.

마지막으로이 함수를 인라인하면 더 나은 결과를 얻을 수 있습니다. 그래도 많이 향상되지는 않습니다 ...

편집 : OJ, 코드를 수정 해 주셔서 감사합니다. 나는 그에 따라 나의 의견을 지웠다


13

`반환 팹 (a-b) <EPSILON;

다음과 같은 경우에 좋습니다 :

  • 입력의 크기 순서는 크게 변하지 않습니다.
  • 매우 적은 수의 반대 부호는 동일하게 취급 될 수 있습니다

그러나 그렇지 않으면 문제가 생길 수 있습니다. 배정도 숫자의 소수 자릿수는 약 16 자리입니다. 비교하는 두 숫자가 EPSILON * 1.0E16보다 큰 경우 다음과 같이 말할 수 있습니다.

return a==b;

첫 번째 문제에 대해 걱정할 필요가 있고 두 번째 문제가 응용 프로그램에 적합하다고 가정하는 다른 접근법을 살펴 보겠습니다. 해결책은 다음과 같습니다.

#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
오타 (f / ()에서 쉼표 ///// 누락 된 쉼표)를 제외하고이 구현에는 EPSILON 내에 있지만 0에 가까운 숫자에는 버그가 있지만 아직은 매우 작습니다. 예를 들어 AreSame (1.0E-10, 1.0E-9)은 상대 오류가 커서 거짓을보고합니다. 당신은 회사의 영웅이됩니다.
brlcad

1
@brlcad 부동 소수점을 얻지 못했습니다 . 1.0E-10과 1.0E-9는 10의 크기가 다릅니다. 따라서 동일하지 않다는 것은 사실입니다. 부동 소수점은 항상 상대 오류 에 관한 것 입니다. 1.0E-10과 1.0E-9를 거의 같은 것으로 생각하는 시스템이 있다면, 둘 다 "제로에 가깝다"(인간에게는 합리적으로 들리지만 수학적으로 아무것도 아님)이므로 EPSILON을 적절하게 조정해야합니다. 그러한 시스템을 위해.
user2261015

8

다른 사람들이 지적했듯이 고정 지수 엡실론 (예 : 0.0000001) 을 사용하는 것은 엡실론 값에서 떨어진 값에는 쓸모없습니다 . 당신의 두 값이 10000.000977 10000 경우 예를 들어,이없는 NO 10000 10000.000977 당신은 아마도 동일한 비트 비트에 대한 않고 얻을 수있는 가까이에 있습니다 -이 두 숫자 사이에 32 비트 부동 소수점 값. 여기서 0.0009 미만의 엡실론은 의미가 없습니다. 직선 항등 연산자를 사용할 수도 있습니다.

마찬가지로, 두 값의 크기가 엡실론에 가까워 질수록 상대 오차는 100 %로 증가합니다.

따라서 0.00001과 같은 고정 소수점 수를 부동 소수점 값 (지수가 임의 인 경우)과 혼합하는 것은 무의미한 운동입니다. 이것은 피연산자 값이 좁은 영역 (즉, 특정 지수에 가까움) 내에 있고 해당 특정 테스트에 엡실론 값을 올바르게 선택한 경우에만 작동합니다. 공중에서 숫자를 빼면 ( "Hey! 0.00001은 작으므로 좋을 것입니다!") 수치 오류가 발생합니다. 나는 나쁜 숫자 코드를 디버깅하는 데 많은 시간을 보냈는데, 일부 schmuck은 임의의 엡실론 값으로 던져져 또 다른 테스트 사례가 작동합니다.

어떤 종류의 수치 프로그래밍을하고 고정 소수점 엡실론에 도달해야한다고 생각 되면 FLOATING-POINT NUMBERS 비교에 대한 BRUCE의 기사를 읽으십시오 .

부동 소수점 숫자 비교


5

Qt 는 두 가지 기능을 구현할 수 있습니다.

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 인 값을 비교해도 작동하지 않으며 값 중 하나가 NaN 또는 무한대 인 값을 비교해도 작동하지 않습니다. 값 중 하나가 항상 0.0이면 qFuzzyIsNull을 대신 사용하십시오. 값 중 하나가 0.0 일 가능성이 높은 경우 한 가지 해결책은 두 값에 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

부동 소수점 숫자의 범용 비교는 일반적으로 의미가 없습니다. 비교하는 방법은 실제로 문제에 달려 있습니다. 많은 문제에서 숫자는 주어진 공차 내에서 숫자를 비교할 수 있도록 충분히 이산화되었습니다. 불행히도 그러한 트릭이 실제로 작동하지 않는 많은 문제가 있습니다. 예를 들어, 관측치가 장벽에 매우 가까운 경우 문제가되는 숫자의 디지털 (스텝) 기능으로 작업하는 것을 고려하십시오 (디지털 스톡 옵션이 떠오름). 공차 기반 비교를 수행하면 문제가 원래 장벽에서 두 개의 새로운 장벽으로 효과적으로 이동하므로 그다지 좋지 않습니다. 다시 말하지만, 이러한 문제에 대한 범용 솔루션은 없으며 특정 솔루션은 안정성을 달성하기 위해 수치 방법을 변경해야합니다.


3

불행히도, "낭비한"코드조차 올바르지 않습니다. EPSILON은 1.0에 더 해지고 값을 변경할 수있는 가장 작은 값입니다 . 1.0 값 은 매우 중요합니다. EPSILON에 추가 할 때 큰 숫자는 변경되지 않습니다. 이제이 값을 비교중인 숫자로 조정하여 값이 다른지 여부를 알 수 있습니다. 두 복식을 비교하는 올바른 표현은 다음과 같습니다.

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

EPSILON질문에 왜 DBL_EPSILON또는 있다고 생각 FLT_EPSILON합니까? 문제는 자신의 상상력에 있으며, DBL_EPSILON그것을 사용하지 않는 코드로 대체했습니다 (실제로 잘못된 선택 일 것입니다).
Ben Voigt

@ BenVoigt, 당신 말이 맞아, 그것은 당시에 내 마음에 있었던 것이었고, 나는 그 질문을 그 관점에서 해석했다.
돈 레바

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.

두 번째 경우 (하나가 하나보다 큼) 두 입력 값은 가능한 한 가깝고 여전히 근접하지 않은 것으로 비교합니다. 따라서 1.0보다 큰 값의 경우 등식 테스트를 사용할 수도 있습니다. 부동 소수점 값을 비교할 때 고정 엡실론이 저장되지 않습니다.


나는 return *(reinterpret_cast<double*>(&x));그것이 일반적으로 작동하지만 실제로는 정의되지 않은 행동 이라고 믿습니다 .
Jaap Versteegh

이 코드는 설명이 필요하지만 numeric_limits<>::epsilonIEEE 754 바닥 점 문제를 설명하기에 충분합니다 .
Steve Hollasch '11

또한 공정한 요점이지만 그러한 통찰력을 기대하는 스택 오버플로에 게시하는 것이 현명한 것은 아닙니다. 이 코드 맹목적으로 복사되어 노조 트릭과 함께 매우 일반적인 패턴을 근절하기가 더 어려워 지므로 모든 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 일 수도 있습니다. 둘째, 두 개의 매우 가까운 부동 소수점 숫자를 빼면 (거의 평등을 찾고 있다고 가정 할 때 경향이 있기 때문에) 치명적인 취소 를 얻는 방법 입니다.

이식성이 좋지는 않지만 grom의 대답은 이러한 문제를 피하는 최선의 방법이라고 생각합니다.


1
좋은 정보를 얻으려면 +1하십시오. 그러나 상대 오차를 증가 시켜서 평등 비교를 망칠 수있는 방법을 알지 못합니다. IMHO 오차는 뺄셈 결과에서만 유의미하지만, 뺄셈되는 두 피연산자에 대한 오차는 평등을 판단하기에 충분히 신뢰할 수 있어야합니다. 해상도가 전체적으로 높아질 필요가 없다면, 유일한 해결책은 가수에서 더 중요한 비트를 가진 부동 소수점 표현으로 이동하는 것입니다.
sehe

거의 동일한 두 숫자를 빼더라도 치명적인 취소가되지는 않습니다. 실제로 전혀 오류가 발생하지 않습니다 (qv Sterbenz 's Theorem). 치명적인 취소는 그 자체 a와 계산 중에 더 일찍 발생합니다 b. 퍼지 비교의 일부로 부동 소수점 빼기를 사용하는 데 전혀 문제가 없습니다 (다른 사람들이 말했듯이 절대 엡실론 값은 주어진 사용 사례에 적합하지 않을 수도 있습니다).
Sneftel

0

실제로 숫자 소프트웨어에는 두 개의 부동 소수점 숫자가 정확히 같은지 확인하려는 경우가 있습니다. 비슷한 질문에 이것을 게시했습니다.

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

따라서 "CompareDoubles1"이 일반적으로 잘못되었다고 말할 수는 없습니다.


과학적 계산이나 수치 분석 배경이없는 사람 (예 : LAPACK, BLAS)이 완전성을 이해하지 못하도록 제한하는 것이 매우 전문적이지만 실제로는 정답에 대한 매우 확실한 참고 자료입니다. 즉, Numerical Recipes 소개 또는 Burden & Faires의 Numerical Analysis 와 같은 내용을 읽은 것으로 가정합니다 .
mctylr

0

비교가 얼마나 정확한지에 달려 있습니다. 정확히 같은 숫자를 비교하려면 ==로 이동하십시오. (실제로 같은 숫자를 원하지 않으면 거의이 작업을 원하지 않습니다.) 적절한 플랫폼에서 다음을 수행 할 수도 있습니다.

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

같은 fabs꽤 빨리되는 경향이있다. 꽤 빠르다는 것은 기본적으로 비트 단위이며 빠르다는 것이 좋습니다.

그리고 double과 float를 비교하는 정수 트릭은 훌륭하지만 다양한 CPU 파이프 라인이 효과적으로 처리하기가 더 어려워지는 경향이 있습니다. 그리고 스택을 자주 사용하는 값의 임시 저장 영역으로 사용하기 때문에 요즘 특정 순서 아키텍처에서는 더 빠르지 않습니다. (관심있는 사람들을위한로드 히트 스토어)


0

수량 규모 측면에서 :

경우 epsilon일부 특정 물리적 의미에서 양의 크기의 작은 부분 (즉, 상대 값)이고 AB유형 I은 다음과 같은 매우 정확하다는 것을, 생각하는 것보다, 같은 의미에서 비교할 수 :

#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
왜 안돼? 설명 할 수 있습니까?
debuti

2
@debuti epsilon는 1과 1 이후의 다음 표현 가능한 숫자 사이의 거리 일뿐 입니다. 기껏해야이 코드는 두 숫자가 서로 정확히 같은지 확인하려고 하지만 2의 비 제곱에을 곱하기 epsilon때문에 제대로 작동하지 않습니다.
Sneftel

2
오, 그리고 std::fabs(std::min(v1, v2))부정확하다-음의 입력의 경우 더 큰 크기의 입력을 선택한다.
Sneftel 2016 년

0

나는 이것을 자바 용으로 쓰지만 아마도 유용하다고 생각할 것이다. 복식 대신 long을 사용하지만 NaN, subnormals 등을 처리합니다.

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

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

작은 프로젝트 에이 기능을 사용했으며 작동하지만 다음 사항에 유의하십시오.

배정도 오차는 당신을 놀라게 할 수 있습니다. epsilon = 1.0e-6, 1.0과 1.000001은 위의 코드에 따라 동일하다고 간주해서는 안되지만 내 컴퓨터에서 기능이 동일하다고 간주하면 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

내 방법은 정확하지 않지만 유용 할 수 있습니다

float를 문자열로 변환 한 다음 문자열 비교를 수행하십시오.

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 : 이봐, 난 이것으로 게임 프로그래밍을하지 않겠지 만,이 문제에 관한 Bruce Dawson의 블로그 (treatise? : D)에서 라운드 트립 플로트 아이디어가 여러 번 나왔다. 방과 누군가가 당신의 머리에 총을 댔을 때 "이봐, 당신은 2 개의 플로트를 X 유효 숫자 내에서 비교해야하는데, 5 분 남았습니다." 이것은 아마도 고려해야 할 것입니다. ;)
shelleybutterfly

@shelleybutterfly 그런 다음 질문은 두 개의 부동 소수점 숫자를 비교 하는 가장 효율적인 방법이었습니다.
Tommy Andersen

@TommyA lol 아마도,하지만 효율성과 관련이없는 이유로 라운드 트립을 다운 베팅했습니다. 내 직감은 HW fp math에 비해 다소 비효율적이지만 소프트웨어 fp의 알고리즘은 적어도 큰 차이가 없을 것입니다. 이 경우 효율성 문제가 심각하다는 것을 보여준 분석을 간절히 기다리고 있습니다. 게다가, 때로는 최적이 아닌 것이 여전히 귀중한 해답이 될 수 있으며, 다운 토트 되었음에도 불구하고 Dawson의 블로그에서 주제에 대해 언급 한 유효한 기술 임에도 불구하고 나는 그것이 가치가 있다고 생각했습니다.
shelleybutterfly

-2

double을 고정과 비교할 수 없습니다 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;
}

-2

보다 일반적인 방법으로 :

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

4
이 방법은 숫자 가 이미 있고 차이 보다 작은 경우 a와 같이 많은 약점이 있습니다. 반대로 숫자가 매우 크면 몇 비트의 오류조차도 숫자를 동일하게 고려하더라도 비교에 실패합니다. 이 답변은 피하고자하는 "일반"비교 알고리즘의 유형입니다. bepsilon()
SirGuy

-3

비트 XOR을 수행하지 않는 이유는 무엇입니까? 해당 비트가 같으면 두 개의 부동 소수점 숫자가 같습니다. 가수보다 지수 비트를 배치하기로 한 결정은 두 개의 부동 수의 비교 속도를 높이기 위해 만들어 졌다고 생각합니다. 나는 많은 답변이 엡실론 비교의 요점을 놓치고 있다고 생각합니다. Epsilon 값은 비교할 부동 소수점 숫자의 정밀도에 따라 다릅니다. 예를 들어, float로 산술을 수행하면 2.5642943554342와 2.5642943554345의 두 숫자가 표시됩니다. 그것들은 같지 않지만 솔루션의 경우 소수점 이하 세 자리 만 중요하므로 2.564와 2.564입니다. 이 경우 엡실론은 0.001과 같습니다. 비트 XOR로 Epsilon 비교도 가능합니다. 내가 틀렸다면 나를 바로 잡으십시오.


여러 질문에 동일한 답변을 추가하지 마십시오. 가장 좋은 것에 대답하고 나머지는 중복으로 표시하십시오. 참조 meta.stackexchange.com/questions/104227/...을
Bhargav 라오

나는 ExOr (그리고 하나 또는 두 개의 비교)을 사용하여 "엡실론 비교"가 가능하다고 생각하지 않으며 심지어 동일한 형식의 정규 표현으로 제한됩니다.
greybeard
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.