671 Stimmen

Welches ist die effektivste Methode für einen Float- und Double-Vergleich?

Was wäre der effizienteste Weg, um zwei double oder zwei float Werte?

Dies einfach zu tun, ist nicht korrekt:

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

Aber so etwas wie:

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

Das scheint eine Verschwendung von Bearbeitungszeit zu sein.

Kennt jemand einen intelligenteren Float-Vergleicher?

0voto

Carlo Wood Punkte 4286

Ich verwende diesen Code. Im Gegensatz zu den obigen Antworten kann man damit eine abs_relative_error Das wird in den Kommentaren des Codes erklärt.

Die erste Version vergleicht komplexe Zahlen, so dass der Fehler in Form des Winkels zwischen zwei "Vektoren" erklärt werden kann Vektoren" gleicher Länge in der komplexen Ebene erklärt werden kann (was ein wenig Einblick). Daraus folgt dann die korrekte Formel für zwei reelle Zahlen folgt.

https://github.com/CarloWood/ai-utils/blob/master/almost_equal.h

Letzteres ist dann

template<class T>
typename std::enable_if<std::is_floating_point<T>::value, bool>::type
   almost_equal(T x, T y, T const abs_relative_error)
{
  return 2 * std::abs(x - y) <= abs_relative_error * std::abs(x + y);
}

donde abs_relative_error ist im Grunde der (doppelte) absolute Wert dessen, was in der Literatur am ehesten definiert wird: ein relativer Fehler. Aber das ist nur die Wahl des Namens.

Was es wirklich ist, sieht man am deutlichsten in der komplexen Ebene, denke ich. Wenn |x| = 1, und y liegt in einem Kreis um x mit Durchmesser abs_relative_error dann werden beide als gleichwertig betrachtet.

0voto

beroso Punkte 379

Allgemeiner formuliert:

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

Anmerkung:
Wie von @SirGuy dargelegt, ist dieser Ansatz fehlerhaft. Ich lasse diese Antwort hier als ein Beispiel, dem man nicht folgen sollte.

0voto

Tomilov Anatoliy Punkte 14605

In Bezug auf die Größenordnung der Mengen:

Si epsilon ist der kleine Bruchteil der Größe einer Menge (d.h. ein relativer Wert) in einem bestimmten physikalischen Sinn und A y B Typen im gleichen Sinne vergleichbar ist, dann denke ich, dass das Folgende durchaus richtig ist:

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

0voto

Michael Lehn Punkte 2583

In numerischer Software gibt es tatsächlich Fälle, in denen man überprüfen möchte, ob zwei Gleitkommazahlen gleich sind genau gleich. Ich habe dies zu einer ähnlichen Frage gepostet

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

Man kann also nicht sagen, dass "CompareDoubles1" generell falsch ist.

0voto

Mat Noguchi Punkte 1064

Das hängt davon ab, wie genau Sie den Vergleich durchführen wollen. Wenn Sie genau dieselbe Zahl vergleichen wollen, verwenden Sie einfach ==. (Das sollten Sie fast nie tun, es sei denn, Sie wollen tatsächlich genau dieselbe Zahl.) Auf jeder anständigen Plattform können Sie auch Folgendes tun:

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

als fabs ist in der Regel ziemlich schnell. Mit ziemlich schnell meine ich, dass es im Grunde ein bitweises UND ist, also sollte es besser schnell sein.

Und Integer-Tricks für den Vergleich von Doubles und Floats sind zwar nett, erschweren aber in der Regel den verschiedenen CPU-Pipelines die effektive Verarbeitung. Und auf bestimmten In-Order-Architekturen ist es heutzutage definitiv nicht schneller, da der Stack als temporärer Speicherbereich für häufig verwendete Werte verwendet wird. (Load-hit-store für diejenigen, die es interessiert.)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X