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?

3voto

Ein allgemeiner Vergleich von Fließkommazahlen ist im Allgemeinen sinnlos. Wie man vergleicht, hängt von dem jeweiligen Problem ab. Bei vielen Problemen sind die Zahlen ausreichend diskretisiert, um sie innerhalb einer bestimmten Toleranz vergleichen zu können. Leider gibt es ebenso viele Probleme, bei denen ein solcher Trick nicht wirklich funktioniert. Ein Beispiel ist die Arbeit mit einer Heaviside-(Stufen-)Funktion einer bestimmten Zahl (man denke an digitale Aktienoptionen), wenn die Beobachtungen sehr nahe an der Barriere liegen. Die Durchführung eines toleranzbasierten Vergleichs würde nicht viel nützen, da sich das Problem von der ursprünglichen Schranke auf zwei neue verlagern würde. Auch hier gibt es keine allgemein gültige Lösung für solche Probleme, und die spezielle Lösung könnte sogar eine Änderung der numerischen Methode erfordern, um Stabilität zu erreichen.

2voto

WaterbugDesign Punkte 29

Meine Klasse, basierend auf den zuvor veröffentlichten Antworten. Sehr ähnlich zu Googles Code, aber ich verwende einen Bias, der alle NaN-Werte über 0xFF000000 schiebt. Das ermöglicht eine schnellere Prüfung auf NaN.

Dieser Code soll das Konzept veranschaulichen und nicht als allgemeine Lösung dienen. Der Code von Google zeigt bereits, wie man alle plattformspezifischen Werte berechnet, und ich wollte das nicht alles duplizieren. Ich habe diesen Code nur begrenzt getestet.

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

1voto

Boojum Punkte 6452

Ich wäre sehr vorsichtig mit jeder dieser Antworten, die eine Fließkomma-Subtraktion beinhaltet (z. B. fabs(a-b) < epsilon). Erstens werden die Fließkommazahlen bei größeren Beträgen spärlicher, und bei ausreichend großen Beträgen, bei denen der Abstand größer als epsilon ist, könnte man genauso gut a == b sagen. Zweitens erhält man durch Subtraktion zweier sehr enger Fließkommazahlen (was in der Regel der Fall sein wird, da man nach annähernder Gleichheit sucht) genau das, was man braucht katastrophale Absage .

Die Antwort von grom ist zwar nicht tragbar, aber ich denke, dass sie diese Probleme am besten vermeidet.

1voto

Prashant Nidgunde Punkte 327

Ich habe eine weitere interessante Implementierung gefunden: 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";
}

0voto

Chunde Huang Punkte 301

Ich verwende diesen Code:

bool AlmostEqual(double v1, double v2)
    {
        return (std::fabs(v1 - v2) < std::fabs(std::min(v1, v2)) * std::numeric_limits<double>::epsilon());
    }

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