Diskussionen zu Gleichheit von Gleitkommazahlen (inkl. etwas Theorie)
Sollte nicht die Prüfung des absoluten Fehlers nur dann erfolgen, wenn es sich um Zahlen verschiedenen Vorzeichens handelt?
Sonst werden besonders kleine Zahlen Grundsätzlich als ähnlich angesehen, selbst wenn sie relativ gesehen sehr unterschiedlich sind.
Hier mein Vorschlag:
public static readonly double Epsilon = Math.Pow(2d, -52d);
public static bool IsEqualTo(this double value, double other) { return IsEqualTo(value, other, Epsilon); }
public static bool IsEqualTo(this double value, double other, double absEps)
{
var specialCase = CheckSpecialCases(value, other);
if (specialCase.HasValue) return specialCase.Value;
if (value >= .0)
{
if (other >= .0)
return value > other
? (value - other) / value < Epsilon
: (other - value) / other < Epsilon;
}
else if (other < .0)
return value > other
? (other - value) / other < Epsilon
: (value - other) / value < Epsilon;
return Math.Abs(value - other) < absEps;
}
private static bool? CheckSpecialCases(double value, double other)
{
if (value == other) return true;
if (double.IsNaN(value)) return double.IsNaN(other);
if (double.IsNegativeInfinity(value)) return double.IsNegativeInfinity(other);
if (double.IsPositiveInfinity(value)) return double.IsPositiveInfinity(other);
if (double.IsNaN(other) || double.IsInfinity(other)) return false;
return null;
}
Gruß
T-Man
Bei der zweiten Variante (Vergleich der signifikanten Stellen) vermute ich ein Problem:
Wenn wir mit Zahlen mit extremen Exponenten arbeiten, können wir sie nach der Multiplikation noch immer nicht in long wandeln. Müssen wir nicht zunächst den Exponenten wegbekommen und erst danach multiplizieren?
T-Man
Hallo,
Hier mein Vorschlag:
Bei Dir ist, z. B., -10 == 10 - das kann so nicht stimmen...
Hallo,
Hier mein Vorschlag:
Bei Dir ist, z. B., -10 == 10 - das kann so nicht stimmen...
Autsch. Es muss natürlich
return Math.Abs(value - other) < absEps;
und nicht
return Math.Abs(value + other) < absEps;
sein.
Danke! Werde es korrigieren.
Hallo,
in welchem Fall genau unterscheidet sich Deine Lösung von der gfoidls?
Machen Deine vielen Fallunterscheidungen nicht genau das gleiche? Math.Abs ist dann doch übersichtlicher.
gfoidl prüft immer zuerst den absoluten Fehler.
Das ist mMn nicht richtig. Diese Prüfung ist nur bei Zahlen verschieden Vorzeichens sinnvoll. gfoidl hat sie ja genau zu dem Zweck eingebaut, dass zwei Zahlen die näherungsweise 0 sind aber sich im Vorzeichen unterscheiden als gleich angesehen werden können.
Wenn man mit gfoidls Methode zwei Zahlen gleichen Vorzeichens mit einem Absolutbetrag kleiner eps_a vergleicht werden sie immer als gleich angesehen. Auch wenn der relative Unterschied sehr groß ist.
Wenn man das verhindern möchte, muss man die Vorzeichen prüfen. Und wenn man das schonmal tut, muss man nicht mehr Abs verwenden.
Die Methode muss auch nicht unbedingt 'übersichtlich' sein. Wichtiger ist doch, dass sie korrekt und möglichst performant ist.
Gruß
T-Man
Eine wichtige Frage, die man sich stellen muss ist, ob man wirklich eine sehr kleine negative und eine sehr kleine positive Zahl als gleich sehen möchte. Das hängt stark vom Kontext ab.
Wenn man dies nicht möchte, sollte man für absEps bzw. eps_a double.Epsilon setzen wenn man wenigstens -0 und 0 als gleich ansehen möchte.
Soll sogar -0 und 0 als verschieden angesehen werden, setzt man 0 für absEps ein.
Hallo T-Man,
man kann da geteilter Meinung sein. Nimm diesen Code:
double d1 = 0.1f;
double d2 = 0.001f;
double d3 = 1 - d1*10;
double d4 = 1 - d2*1000;
Console.WriteLine (d3);
Console.WriteLine (d4);
Die Ausgabe ist
-1,49011611938477E-08
-4,74974513053894E-08
beides kleine Zahlen mit gleichem Vorzeichen und einem ziemlichen großen relativen Fehler, gerade weil die (absoluten) Zahlen so klein sind. Ohne Rundungsfehler käme in beiden Fällen Null (0) heraus. Man würde sich also hier erhoffen, dass IsEqualsTo true liefert. Tut deine Implementierung aber gerade nicht. Der absolute Fehler kann also durchaus eine Rolle spielen, nicht nur wenn das Vorzeichen unterschiedlich ist.
herbivore
PS: Unsere Antworten haben sich überschnitten. Meine bezieht sich auf deinen Beitrag davor. Kontext ist der richtige Begriff. Es gibt hier kein pauschales Richtig oder Falsch, sondern es hängt vom Kontext ab, was gewünscht ist.
Guter Einwand. Das zeigt nochmal, dass man genau wissen muss, was man eigentlich möchte.
gfoidls Metode liefert übrigens auch nur dann true zurück, wenn man das eps_a entsprechend groß wählt.
Bitte keine Full-Quotes.
Hallo,
kurze Erklärung:
Das Snippet werde ich dann anpassen/ändern, wenn ein Testfall zeigt dass es ein falsches Verhalten besitzt und das nicht auf einen "Bedienfehler" zurückzuführen ist.
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"