什么是浮点和双重比较最有效的方法?

什么是比较两个double float值或两个float值的最有效方法?

简单地做这件事是不正确的:

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

但是像这样:

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

似乎浪费处理。

有谁知道更聪明的浮点比较器?


使用任何其他建议时要格外小心。 这一切都取决于上下文。

我花了很长时间来追踪系统中的一个错误,假设a==b如果|ab|<epsilon 。 根本问题是:

  • 如果a==bb==ca==c ,算法中隐含的推定。

  • 对于以英寸为单位的线和以密耳(.001英寸)为单位的线使用相同的epsilon。 那是a==b但是1000a!=1000b 。 (这就是为什么AlmostEqual2sComplement要求epsilon或max ULPS)。

  • 对于角度的余弦和线的长度使用相同的epsilon!

  • 使用这种比较函数对集合中的项目进行排序。 (在这种情况下,使用内置的C ++运算符==双打产生正确的结果。)

  • 就像我说的:这一切都取决于上下文和ab的预期大小。

    顺便说一句, std::numeric_limits<double>::epsilon()是“机器epsilon”。 它是1.0和下一个可用double表示的值之间的差值。 我想它可以在比较函数中使用,但只有当期望值小于1时(这是对@ cdv的答案的回应......)

    另外,如果你基本上int算术doubles (这里我们用双打来保存在某些情况下INT值)的算术将是正确的。 例如4.0 / 2.0将与1.0 + 1.0相同。 只要你不做导致分数的事情(4.0 / 3.0),或者不要超出整数的大小。


    与epsilon值的比较是大多数人所做的(甚至在游戏编程中)。

    你应该稍微改变你的实现:

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

    编辑:Christer在最近的博客文章中为这个主题添加了一堆很棒的信息。 请享用。


    我发现Google C ++测试框架包含一个很好的跨平台基于模板的AlmostEqual2sComplement实现,它可以用于双打和浮动。 鉴于它是根据BSD许可证发布的,只要您保留许可证,在自己的代码中使用它应该没有问题。 我从http://code.google.com/p/googletest/source/browse/trunk/include/gtest/internal/gtest-internal.h中提取了以下代码,并在顶部添加了许可证。

    请确保#定义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岁。 它可能仍然有效,代码很好,但有些人找到了改进。 最好从Google Test源代码中获取最新版本的AlmostEquals ,而不是我在这里粘贴的那个。

    链接地址: http://www.djcxy.com/p/27435.html

    上一篇: What is the most effective way for float and double comparison?

    下一篇: Sass Variable in CSS calc() function