Какой самый эффективный способ сравнения с плавающей запятой и двойного сравнения? - PullRequest
477 голосов
/ 20 августа 2008

Какой самый эффективный способ сравнить два 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);
}

Кажется, переработка отходов.

Кто-нибудь знает более умный компаратор поплавка?

Ответы [ 27 ]

0 голосов
/ 16 сентября 2016

В более общем виде:

template <typename T>
bool compareNumber(const T& a, const T& b) {
    return std::abs(a - b) < std::numeric_limits<T>::epsilon();
}
0 голосов
/ 06 февраля 2016

Нельзя сравнивать два 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;
}
0 голосов
/ 04 марта 2014
/// 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 и на этот раз получаю ожидаемый результат.

0 голосов
/ 03 декабря 2013

Мой путь может быть не правильным, но полезным

Преобразование обоих чисел с плавающей точкой в ​​строки и затем сравнение строк

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;

}

Оператор перекрытия также может быть сделано

0 голосов
/ 29 августа 2013

В пересчете на шкалу количеств:

Если 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 голосов
/ 11 июня 2012

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

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

Так что нельзя сказать, что "CompareDoubles1" вообще неверно.

0 голосов
/ 08 августа 2010

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...