Какой самый эффективный способ сравнения с плавающей запятой и двойного сравнения?

Если деструктор базового класса - private или protected, вы не можете вызвать delete с помощью указателя базового класса.

Использовать защищенный деструктор для предотвращения уничтожения производного объекта через указатель базового класса. Он ограничивает доступ к destuctor для производных классов. И это предотвращает автоматические (стековые) объекты базы классов.

В действительности используется , чтобы разрешить любое другое полиморфное использование производных классов с помощью указателей, но не позволяют пользователям удалять с помощью такого указателя. Пример: - Абстрактные базовые классы / интерфейсы.

Но деструктор protected, non-virtual, кажется, ждет ошибки. Предполагая, что вы не предоставляете функцию destroy(), вы должны в конечном итоге сделать dtor public. Как только вы это сделаете, у вас больше нет контроля над классом, и вы рискуете получить полиморфное удаление с помощью не виртуального dtor, если кто-то получит дальнейшее развитие от вашего класса.

483
задан Kirill Kobelev 21 December 2016 в 03:17
поделиться

12 ответов

Это зависит от того, как точный Вы хотите, чтобы сравнение было. Если Вы хотите выдержать сравнение для точно того же числа, то просто идут с ==. (Вы почти никогда не хотите сделать это, если Вы на самом деле не хотите точно то же число.) На любой достойной платформе можно также сделать следующее:

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

как fabs имеет тенденцию быть довольно быстрым. Довольно быстрым я подразумеваю, что это - в основном поразрядное И, таким образом, это лучше быть быстрым.

И целочисленные приемы для сравнения удваивается, и плавания хороши, но имеют тенденцию делать более трудным для различных конвейеров ЦП обработать эффективно. И это находится определенно не быстрее на определенном, чтобы архитектура в эти дни из-за использования стека как область временного хранения для значений, которые используются часто. (Хранилище хита загрузки для тех, кто заботится.)

1
ответ дан Tim Cooper 21 December 2016 в 03:17
поделиться

Сравнение со значением эпсилона - то, что большинство людей делает (даже в игровом программировании).

необходимо изменить реализацию немного хотя:

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

Редактирование: Christer добавил стопку большой информации об этой теме на недавнее сообщение в блоге . Наслаждаться.

172
ответ дан Trevor Hickey 21 December 2016 в 03:17
поделиться

Для больше подробно подход читал Сравнение чисел с плавающей точкой . Вот фрагмент кода из той ссылки:

// 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;    
}
46
ответ дан grom 21 December 2016 в 03:17
поделиться

Портативный способ получить эпсилон в 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();
}
27
ответ дан Chris de Vries 21 December 2016 в 03:17
поделиться

'возврат fabs (-b) < ЭПСИЛОН;

Это прекрасно если:

  • порядок величины Ваших исходных данных не изменяется очень
  • , очень небольшие числа противоположных знаков можно рассматривать как равные

, Но иначе он приведет Вас в проблему. Числа двойной точности имеют разрешение приблизительно 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 будет достаточен, прекрасный - используют его! Но знайте о его недостатках и других возможных решениях также.

14
ответ дан 21 December 2016 в 03:17
поделиться

Код, который Вы записали, прослушивается:

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

корректный код был бы:

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

(... и да это отличается)

интересно, не заставил ли fabs Вас потерять отложенные вычисления в некотором случае. Я сказал бы, что это зависит от компилятора. Вы могли бы хотеть попробовать обоих. Если они эквивалентны в среднем, берут реализацию с fabs.

, Если у Вас есть некоторая информация, на которой из этих двух плаваний, более вероятно, будет больше, чем тогда другой, можно играть на порядке сравнения воспользоваться лучшим преимуществом отложенных вычислений.

Наконец Вы могли бы получить лучший результат путем встраивания этой функции. Вряд ли улучшиться очень хотя...

Редактирование: OJ, благодарит исправить Ваш код. Я стер свой комментарий соответственно

14
ответ дан fulmicoton 21 December 2016 в 03:17
поделиться

Сравнение общего назначения чисел с плавающей запятой вообще бессмысленно. Как выдержать сравнение, действительно зависит от проблемы под рукой. Во многих проблемах числа достаточно дискретизируются, чтобы позволить сравнивать их в рамках данного допуска. К сожалению, существуют так же, как много проблем, где такой прием действительно не работает. Для одного примера рассмотрите работу с Heaviside (шаг) функция рассматриваемого числа (цифровые фондовые опционы приходят на ум), когда Ваши наблюдения очень близко к барьеру. Выполнение основанного на допуске сравнения не сделало бы много хорошего, поскольку это эффективно сместит проблему от исходного барьера до двух новых. Снова, нет никакого решения общего назначения для таких проблем, и конкретное решение могло бы потребовать движения до изменения численного метода для достижения устойчивости.

3
ответ дан 21 December 2016 в 03:17
поделиться

Сравнение чисел с плавающей точкой для зависит от контекста. Начиная с ровного изменения порядка операций может привести к различным результатам, важно знать, как "равный" Вы хотите, чтобы числа были.

Сравнение чисел с плавающей точкой Bruce Dawson является хорошим местом для запуска при рассмотрении сравнения с плавающей точкой.

следующие определения от искусство программирования 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.

96
ответ дан Damian 21 December 2016 в 03:17
поделиться

Будьте чрезвычайно тщательным использованием любого из других предложений. Все это зависит от контекста.

я провел долгое время, прослеживая ошибки в системе, которая предположила a==b если |a-b|<epsilon. Базовые проблемы были:

  1. неявное предположение в алгоритме, что, если a==b и b==c тогда a==c.

  2. Используя тот же эпсилон для строк, измеренных в дюймах и строках, измеренных во фрезах (.001 дюйм). Это a==b, но 1000a!=1000b. (Поэтому AlmostEqual2sComplement просит эпсилон или макс. ULPS).

  3. использование того же эпсилона и для косинуса углов и для длины строк!

  4. Используя такой сравнивать функцию с элементами сортировки в наборе. (В этом случае с помощью встроенного оператора C++ == для удваивается, привел к корректным результатам.)

Как я сказал: все это зависит от контекста и ожидаемого размера a и b.

BTW, std::numeric_limits<double>::epsilon() является "эпсилоном машины". Вот в чем разница между 1,0 и следующее значение, представимое двойным. Я предполагаю, что это могло использоваться в сравнить функции, но только если математические ожидания - меньше чем 1. (Это в ответ на ответ @cdv...)

кроме того, если Вы в основном имеете int арифметика в [1 111] (здесь, мы используем, удваивается для содержания международных значений в определенных случаях) арифметика будет корректна. Например, 4.0/2.0 совпадет с 1.0+1.0. Это, пока Вы не делаете вещей, которые приводят к частям (4.0/3.0) или не выходят на улицу из размера интервала

427
ответ дан Csaba Toth 21 December 2016 в 03:17
поделиться

Некоторые эксперименты с ответами.

#ifndef FLOATING_POINT_MATH_H
#define FLOATING_POINT_MATH_H

#ifdef _MSC_VER
#pragma once
#endif

#include <type_traits>
#include <cmath>

/** Floating point math functions with custom tolerance

@param float, double, long double.
@return bool test result.

is_approximately_equal(a, b, tolerance)
is_essentialy_equal(a, b, tolerance)
is_approximately_zero(a, tolerance)
is_definitely_greater_than(a, b, tolerance)
is_definitely_less_than(a, b, tolerance)
is_within_tolerance(a, b, tolerance)
*/

namespace
{
    template<typename T> constexpr T default_tolerance = std::numeric_limits<T>::epsilon();
    template<typename T> using Test_floating_t = typename std::enable_if<std::is_floating_point<T>::value>::type;
}



template<typename T, typename = Test_floating_t<T>> inline constexpr 
bool is_approximately_equal(const T a, const T b, const T tolerance = default_tolerance<T>) noexcept
{
    return std::fabs(a - b) <= ((std::fabs(a) < std::fabs(b) ? std::fabs(b) : std::fabs(a)) * tolerance);
}



template<typename T, typename = Test_floating_t<T>> inline constexpr 
bool is_essentialy_equal(const T a, const T b, const T tolerance = default_tolerance<T>) noexcept
{
    return std::fabs(a - b) <= ((std::fabs(a) > std::fabs(b) ? std::fabs(b) : std::fabs(a)) * tolerance);
}



template<typename T, typename = Test_floating_t<T>> inline constexpr 
bool is_approximately_zero(const T a, const T tolerance = default_tolerance<T>) noexcept
{
    return std::fabs(a) <= tolerance;
}



template<typename T, typename = Test_floating_t<T>> inline constexpr 
bool is_definitely_greater_than(const T a, const T b, const T tolerance = default_tolerance<T>) noexcept
{
    return (a - b) > ((std::fabs(a) < std::fabs(b) ? std::fabs(b) : std::fabs(a)) * tolerance);
}



template<typename T, typename = Test_floating_t<T>> inline constexpr 
bool is_definitely_less_than(const T a, const T b, const T tolerance = default_tolerance<T>) noexcept
{
    return (b - a) > ((std::fabs(a) < std::fabs(b) ? std::fabs(b) : std::fabs(a)) * tolerance);
}



template<typename T, typename = Test_floating_t<T>> inline constexpr 
bool is_within_tolerance(const T a, const T b, const T tolerance = default_tolerance<T>) noexcept
{
    return std::fabs(a - b) <= tolerance;
}



#endif

//is_approximately_equal(0.101, 0.1, 0.01); // true
//is_essentialy_equal(0.1, 0.1, 0.01); // true
//is_essentialy_equal(0.101, 0.1, 0.01); // false
//is_essentialy_equal(0.101, 0.101, 0.01); // true
//is_approximately_equal(1, 1); // No instance matches arg...
0
ответ дан 22 November 2019 в 22:39
поделиться

Я обнаружил, что Google C ++ Testing Framework содержит хорошую кроссплатформенную реализацию на основе шаблонов почтиEqual2sComplement, которая работает как с числами типа double, так и с числами с плавающей запятой. Учитывая, что он выпущен под лицензией 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 года. Вероятно, он все еще в силе, и код хороший, но некоторые люди нашли улучшения. Лучше всего скачайте последнюю версию NearEquals прямо из исходного кода Google Test, а не ту, которую я здесь вставил.

113
ответ дан 22 November 2019 в 22:39
поделиться

Я бы очень опасался любого из этих ответов, которые включают вычитание с плавающей запятой (например, fabs (a-b) катастрофическое сокращение .

Хотя ответ grom не является переносимым, я думаю, что он лучше всего помогает избежать этих проблем.

0
ответ дан 22 November 2019 в 22:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: