Wie vergleiche ich exakt zwei Floats?
-
Hi,
ich Narr wollte doch tatsächlich zwei Floats per == vergleichen.
Kennt wer ein paar Funktionen die einen Vergleich zweier Floats auf Gleichheit sowie Größer, Kleiner exakt hin bekommen?
-
float genauigkeit = 0.001; if (abs(x - y) <= genauigkeit) { }
-
Exakt tut man floats doch tatsächlich mit == vergleichen.
Meinst Du vielleicht "näherungsweise vergleichen"?
-
== vergleicht 2 Floats exakt. Es entsteht nur das Problem, dass Floats sehr selten exakt gleich sind, weshalb das eine ziemlich nutzlose Funktionalität ist.
Beispiel:if (10*0.1f == 1.f) //tu was
Das "==" ist schon exakt, aber 10*0.1 ist (je nach Compiler und Plattform) nicht exakt 1.f, deswegen wird der "tu was" Code nicht ausgeführt.
-
template<typename Float> bool equal( typename std::enable_if<std::is_floating_point<Float>::value, Float>::type _1, typename std::enable_if<std::is_floating_point<Float>::value, Float>::type _2, Float epsilon = std::numeric_limits<Float>::epsilon()) { return std::abs(_1 - _2) <= epsilon; }
Sollte so passen denke ich.
-
Danke das hilft mit schon mal. Ja, um exakt zu sein will ich näherungsweise vergleichen
Es scheint hier darauf hin zu laufen, dass ich mich für eine Genauigkeit entscheiden muss, die ich dann als gleich definiere.
@Ethon_: Sieht ja mal heftig aus. Danke dafür, kannst du es auch erklären ich blicke da nicht ganz durch?
-
Ethon_ schrieb:
template<typename Float> bool equal( typename std::enable_if<std::is_floating_point<Float>::value, Float>::type _1, typename std::enable_if<std::is_floating_point<Float>::value, Float>::type _2, Float epsilon = std::numeric_limits<Float>::epsilon()) { return std::abs(_1 - _2) <= epsilon; }
Sollte so passen denke ich.
Nein, epsilon hat eine ganz andere Bedeutung als du denkst. Und Fließkommazahlen funktionieren auch anders, als du anscheinend denkst.
double a = 1e-45; double b = 2e-45; std::cout << a << " == " << b << "? " << (equal<double>(a,b) ? "Ja!\n" : "Nein!\n");
1e-45 == 2e-45? Ja!
-
Das Epsilon ist die kleinste darstellbare Differenz zwischen 2 unterschiedlichen Gleitkommazahlen.
Ich denke ich weiß wie Gleitkommazahlen funktionieren, musste den Käse mit der IEEE-Darstellung lange genug händisch machen.
Ja, vermutlich ist epsilon ungeeignet für den Vergleich. Muss man halt eine beliebige, sehr kleine Zahl verwenden. Habe aus genau diesem Grund den Wert nicht hartkodiert.
@Ethon_: Sieht ja mal heftig aus. Danke dafür, kannst du es auch erklären ich blicke da nicht ganz durch?
Ein Template, damit die Funktion mit allen Gleitkommazahlen (float, double und long double) funktioniert. Das enable_if, damit die Funktion für den Compiler nicht in Betracht gezogen wird, wenn sie zb mit 2 Integern aufgerufen wird.
Der dritte Parameter soll den "Puffer" bei dem Vergleich angeben. Epsilon ist wie Sepp bereits gesagt hat falsch, ich überlasse es also der heiligen Forenschaft, die größte Differenz anzugeben, die zwischen einer beliebigen Gleitkommazahl und deren Nachfolger existieren kann. Der Wert wäre dann vermutlich am geschicktesten zu verwenden, auch wenn er möglicherweise immer noch daneben ist, die Darstellungsfehler summieren sich ja auf, wenn du mehrere Rechnungsschritte hintereinander machen musst. Wenn Genauigkeit nicht so notwendig ist, nimm einfach 0.000001 und gut.
-
Hier habe ich was gefunden auf cppreference.com. Nutzt das jemand so:
#include <cmath> #include <limits> #include <iomanip> #include <iostream> #include <type_traits> 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 larger value // and multiplied by the desired precision in ULPs (units in the last place) return std::abs(x-y) <= std::numeric_limits<T>::epsilon() * std::max(std::abs(x), std::abs(y)) * ulp; } int main() { double d1 = 0.2; double d2 = 1 / std::sqrt(5) / std::sqrt(5); 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"; }
-
Ethon_ schrieb:
Habe aus genau diesem Grund den Wert nicht hartkodiert.
Dann hätte ich den besser weggelassen. - Defaultparameter tendieren genommen zu werden, wenn man nicht darüber nachdenken will was man angeben soll.
Das sollte man aber bei so Zeugs immer tun.
-
Ethon_ schrieb:
Das Epsilon ist die kleinste darstellbare Differenz zwischen 2 unterschiedlichen Gleitkommazahlen.
Nein, es ist der Unterschied zwischen 1 und der nächstgrößeren Zahl. Das heißt, man ist hier schon festgelegt auf die Größenordnung 1. Außerdem ist der kleinstmögliche Unterschied sicherlich nicht das, was man möchte. Die akkumulierten Ungenauigkeiten in längeren Recnhungen sind meistens deutlich größer. Mit etwas Numerik kann man sich (für eine bestimmte Rechnung) auch ausrechnen, mit welcher Fehler ungefähr zu erwarten ist.
Wenn Genauigkeit nicht so notwendig ist, nimm einfach 0.000001 und gut.
Wieder ziemlicher Unsinn. Wie willst du wissen, dass der Threadersteller in dieser Größenordnung rechnet? Der Witz an Fließkommazahlen ist doch gerade, dass sie in allen Größenordnungen gleich gut funktionieren, egal ob ich mit Zahlen um 1 herum rechne, mit Centillionen, oder mit 10^(-48).
Daher gibt es leider keine einfache Antwort auf die Frage nach dem "richtigen" Vergleich, die genannten sind aber jedenfalls falsch. Siehe auch:
http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
-
Ich glaube, man kann ganze Papers über das Vergleichen von Gleitkommazahlen schreiben^^
-
Lichtweite schrieb:
Kennt wer ein paar Funktionen die einen Vergleich zweier Floats auf Gleichheit sowie Größer, Kleiner exakt hin bekommen?
"exakt"? Was meinst du? Mit floats kannst du eben nicht exakt rechnen. Wie so ein vergleich aussehen sollte, hängt immer stark vom zu lösenden Problem und dem Ansatz ab. Mach dir klar, dass Du die ganze Zeit Rundungsfehler mitschleppst und immer neue dazu kommen. Wenn du zwei Zahlen addierst oder subtrahierst, addieren sich auch die absoluten Fehler. Wenn du zwei Zahlen multiplizierst oder dividierst, addieren sich die relativen Fehler.
Du kannst ja mal die Invervall-Bibliothek von Boost ausprobieren.
http://www.boost.org/doc/libs/1_52_0/libs/numeric/interval/doc/interval.htm
Dann bekommst du vielleicht ein Gefühlt dafür, was du am Ende deiner Berechnungen im schlimmsten Fall für akkumulierte Rundungsfehler hast.Wenn du ein Problem zu lösen versuchst, zu dem es schon bekannte, numerisch stabile Algorithmen gibt, dann solltest du dich da mal umgucken, wie die Numerik-Gurus das gemacht haben.
-
Das mit dem exakt hatte ich schon revidiert^^ Ich habe jetzt mein gepostetes Beispiel von cppreference.com in meine Klasse implementiert und dies reicht mir erst einmal.
Ich will mich jetzt auch nicht mit dem doch komplexen Thema länger aufhalten und danke allen für die Hilfe.