Frage zu Ungenauigkeit bei Fließkommazahlen
-
Du meinst ungefähr sowas?
float a,b; a = /* eine Berechnung */; /* hier den Rundungsmodus verändern */ b = a;wird übersetzt in etwas wie
float a, b; internal_double temp = /* eine Berechnung */; a = temp; /* hier den Rundungsmodus verändern */ b = temp;?
Ich würde sagen, dass das nicht zulässig ist. Der C99-Standard (C++ sagt dazu nichts) erwähnt die Möglichkeit, innerhalb eines Ausdrucks Rundungen wegzulassen:
"A floating expression may be contracted, that is, evaluated as though it were an atomic operation, thereby omitting rounding errors implied by the source code and the expression evaluation method." Und: "The FP_CONTRACT pragma in <math.h> provides a way to disallow contracted expressions."Aber nichts darüber, dass das auch ausdrucksübergreifend möglich ist. Die Zuweisung a=b erfordert keine Rundung, also darf der Rundungsmodus an der Stelle auch keine Auswirkungen haben.
-
TGGC schrieb:
Ich hoffe du sagst das jetzt nicht, weil du denkst, das ein "a=b" wie ein "memcpy(p1,p2,sizeof(float))" und ein "if(a==b)" ein "memcmp(p1,p2,sizeof(float))". Das ist jedenfalls nicht so. Schau dir den Assemblercode an, es wird alles auf der FPU gemacht.
Öh, FPU-Instruktionen kann ich nicht lesen
Ich bin nicht von bitweiser Gleichheit ausgegangen, ich weiß schon, dass == die Werte arithmetisch vergleicht, und ich gehe davon aus, dass gleiche Werte, auch wenn sie verschieden repräsentiert sein mögen, als gleich erkannt werden (sonst könnte man sich den Gleichheitsoperator auch sparen.)
An NaN hab ich allerdings nicht gedacht, guter Einwand. Das hat aber mit "Rauschen" natürlich nichts zu tun.Das mit dem Vertrauen ist so eine Sache. Dass man sich auf Gleichheit von Fließkommazahlen verlässt, kommt ja eher selten vor, deshalb wird mein Vertrauen in diese Sache nicht besonders in Anspruch genommen, aber bevor ich tatsächlich Code schreibe, der ein eingebildetes Problem umgehen soll, mach ich mich lieber über die realen Verhältnisse schlau.
-
Wenn du´s 100%ig korrekt machen willst geht Folgendes:
float a = 0.3456f; float b = a; if( std::abs( a - b ) < std::numeric_limits<float>::epsilon() ) { // a == b } else { // a != b }
-
Das ist nichtmal 1% korrekt.
-
So?
-
std::numeric_limits<float>::epsilon() liegt in der Größenordnung 10^-7, was sagt das aus über zwei floats in der Größenordnung 10^10? Was für zwei floats in der Größenordnung 10^-24?
-
Wo ist das Problem? Die Frage ist doch, wann zwei float Werte als gleich angesehen werden können, und wenn man die Differenz von zwei float Werten bildet spielen die absoluten Grössen keine Rolle mehr, weil man die Differenz mit epsilon vergleicht.
-
Du meinst also, dass 1e-20 und 1e-30 gleich sind?
-
@Bashar Sofern epsilon > 1e-20 ist, ja!
Das ganze hier ist eine Grenzwertbetrachtung und wer in Mathe nicht aufgepasst hat, versteht das nicht. Es gilt
a + e == aDas Epsilon kannst du manuell berechnen (C#)
double tau = 1.0; double walt = 1.0; double wneu = 0.0; while (wneu != walt) { tau *= 0.5; wneu = walt + tau; } return 2.0 * tau;Quelle: http://dotnet-snippets.de/dns/c-maschinengenauigkeit-SID961.aspx
Gruß,
Thomas
-
@Siassei: Mach dich nicht zum Obst... Denk lieber noch mal nach...
epsilon *2^potenz würde (ganz grob gesehen) vll gehen, aber nur epsilon def. nicht, weil es eben eine _gleit_kommazahl ist...
-
Siassei schrieb:
@Bashar Sofern epsilon > 1e-20 ist, ja!
Epsilon ist für float ungefähr 1e-7.
Das ganze hier ist eine Grenzwertbetrachtung und wer in Mathe nicht aufgepasst hat, versteht das nicht.
Nein, es ist keine Grenzwertbetrachtung.
Es gilt
a + e == aFür a >= 2, ja.
-
Touché... und jetzt frage ich mich, wofür epsilon() und denorm_min() überhaupt gut sind. epsilon() ist mit 1.9E-7 deutlich zu gross und denorm_min() mit ~1.41E-45 liegt ausserhalb des float Wertebereichs...
-
DocShoe schrieb:
Touché... und jetzt frage ich mich, wofür epsilon() und denorm_min() überhaupt gut sind. epsilon() ist mit 1.9E-7 deutlich zu gross und denorm_min() mit ~1.41E-45 liegt ausserhalb des float Wertebereichs...
Das epsilon gibt dir einen Anhaltspunkt, wieviele zählende Stellen die Fließkommazahl hat. Und denorm_min ist wie der Name schon sagt die kleinste darstellbare Zahl.
Immer daran denken, dass eine Fließkommazahl einen normalen Zahlenteil (die Mantisse) und einen Exponententeil hat. Das epsilon beschreibt dir die Genauigkeit der Mantisse und das denorm_min die Genauigkeit des Exponenten.
-
static T epsilon() throw();
20 Machine epsilon: the difference between 1 and the least value greater than 1 that is representable.Anders ausgedrückt:
a!=b g.d.w. a/b >= e/radix , mit normalen a,b: a > b > 0
es ist damit die Obergrenze für den relativen Fehler einer normalisierten Zahl.Nicht, dass klar wäre, wieso diese Diskussion hier geführt werden muss. Mit der Ausgangsfrage hat das nichts zu tun. Und irgendwelche vagen Hinweise auf FPUs helfen hier auch nicht.
Nur weil jemand dem Compiler nicht traut, führt das zweifellos nicht zur Lösung.
-
camper schrieb:
static T epsilon() throw();
Machine epsilon: the difference between 1 and the least value greater than 1 that is representable.Anders ausgedrückt:
a!=b g.d.w. a/b >= e/radix , mit normalen a,b: a > b > 0Du meinst wohl abs(a/b-1).
Finde es aber schon komisch, dass Du != unter Verwendung von / und >= "definierst".
Ich weiß nicht, welche Garantien hier wirklich gemacht werden, aber man sollte doch meinen, dass die Vergleichsoperatoren auf Floatingpoint-Zahlen genauso funktionieren, wie man es erwartet: nämlich einfach die Werte vergleichen, die durch die Bitmuster repräsentiert werden.
Gruß,
SP
-
Sebastian Pizer schrieb:
Du meinst wohl abs(a/b-1).
Finde es aber schon komisch, dass Du != unter Verwendung von / und >= "definierst".
ja, -1
Allerdings soll das keine Definition von != sein, sondern einer Erläuterung hinsichtlich des Sinns von epsilon.TGGC schrieb:
Sebastian Pizer schrieb:
nämlich einfach die Werte vergleichen, die durch die Bitmuster repräsentiert werden.
Womit wir wieder beim eigentlichen Problem sind, schreibt jede Hardware in jedem Zustand fuer einen Wert immer genau das gleiche Bitmuster in den Speicher und liest sie aus jedem Bitmuster im Speicher immer genau den gleichen Wert? Wenn man etwas davon durch die moeglichen Rundungsmodi etc. beeinflussen kann, dann kann die oben genannte Aktion scheitern.
Beispiel? Hardware ist hier nicht besonders relevant; jedenfalls scheint hier eine merkwürdige (ohne gar keine) Vorstellung von "Wert" zu bestehen. (Implizite) Rundung tritt grundsätzlich dann auf, wenn ein Wert eines bestimmten Typen T in einen Wert des Typs U != T umgewandelt werden muss, und nicht jeder mögliche Wert des Typs T exakt durch einen Wert des Typs U dargestellt werden kann. Bei einer Zuweisung eines Wertes an eine Variable des gleichen Typs ist für Rundung schlicht weder Veranlassung noch Platz.
Wird der (wohldefinierte - keine Traprepräsentation) Wert einer POD-Variablen a einer anderen Variablen b gleichen Typs zugewiesen, so sind im Anschluss die Werte beider Variablen äquivalent: in jedem Ausdruck, der nur vom Wert der Variablena abhängt, kann nach Belieben jede Instanz von a durch b ersetzt werden, ohne dass sich der Wert des Ausdrucks ändert.
Die Frage des OP ist insoweit positiv zu beantworten: der Vergleich der Variablen mit == wird immer true ergeben soweit == Äquivalenzrelation ist.
Der Vergleich zwischen einem NaN und einem beliebigen anderen Wert führt immer zum Ergebnis ungleich; insoweit ist == keine Äquivalenzrelation.
-
camper schrieb:
Der Vergleich zwischen einem NaN und einem beliebigen anderen Wert führt immer zum Ergebnis ungleich; insoweit ist == keine Äquivalenzrelation.
Das sehe ich gerade nicht. Meinste Du "Der Vergleich zwischen einem NaN und einem beliebigen Wert" oder wie geschrieben "Der Vergleich zwischen einem NaN und einem beliebigen anderen Wert"?
-
volkard schrieb:
camper schrieb:
Der Vergleich zwischen einem NaN und einem beliebigen anderen Wert führt immer zum Ergebnis ungleich; insoweit ist == keine Äquivalenzrelation.
Das sehe ich gerade nicht. Meinste Du "Der Vergleich zwischen einem NaN und einem beliebigen Wert" oder wie geschrieben "Der Vergleich zwischen einem NaN und einem beliebigen anderen Wert"?
ja, danke. Entscheidend ist hier der Vergleich des Wertes mit sich selbst, der ungleich liefert.
-
-
TGGC schrieb:
Sebastian Pizer schrieb:
nämlich einfach die Werte vergleichen, die durch die Bitmuster repräsentiert werden.
Womit wir wieder beim eigentlichen Problem sind, schreibt jede Hardware in jedem Zustand fuer einen Wert immer genau das gleiche Bitmuster in den Speicher
Ich verstehe ehrlich gesagt nicht, was Du damit sagen willst. Das liegt wahrscheinlich daran, dass wir unter "Zustand", "Wert", "Bitmuster" etwas anderes verstehen. Dass Du zwischen "Zustand" und "Bitmuster" unterscheidest irritiert mich gerade.
Der C++ Standard spricht hier von "object representation" und "value representation". Ferner: "For POD types, the value representation is a set of bits in the object representation that determines the value, which is one discrete element of an implementation-defined set of values."
Und "implementation-defined" sagt schon mal viel aus. Diese Wertemenge kann man ja definieren, wie man will. Sind +0 und -0 zwei "verschiedene" Elemente dieser Menge? Wenn ja, wie ist die Ordnung definiert? -0 < +0 oder -0 == +0? Bei IEEE754 wird zwischen -0 und +0 unterschieden (ein Unterlauf erhält das Vorzeichen) wobei aber -0 und +0 als gleichwertig (bzgl Vergleichsoperatoren) betrachtet werden.
Gruß,
SP