Heute habe ich einen C ++ - Code (von jemand anderem geschrieben) durchgesehen und diesen Abschnitt gefunden:
double someValue = ...
if (someValue < std::numeric_limits<double>::epsilon() &&
someValue > -std::numeric_limits<double>::epsilon()) {
someValue = 0.0;
}
Ich versuche herauszufinden, ob das überhaupt Sinn macht.
Die Dokumentation für epsilon()
sagt:
Die Funktion gibt die Differenz zwischen 1 und dem kleinsten Wert größer als 1 zurück, der darstellbar ist [durch ein Doppel].
Gilt dies auch für 0, dh epsilon()
ist der kleinste Wert größer als 0? Oder gibt es Zahlen zwischen 0
und 0 + epsilon
die durch a dargestellt werden können double
?
Wenn nicht, ist der Vergleich dann nicht gleichbedeutend mit someValue == 0.0
?
numeric_limits<>::epsilon
irreführend und irrelevant. Wir wollen 0 annehmen, wenn der tatsächliche Wert nicht mehr als einige ε von 0 abweicht. Und ε sollte basierend auf der Problemspezifikation und nicht basierend auf einem maschinenabhängigen Wert ausgewählt werden. Ich würde vermuten, dass das aktuelle Epsilon nutzlos ist, da bereits wenige FP-Operationen einen größeren Fehler verursachen können.Antworten:
Unter der Annahme eines 64-Bit-IEEE-Doppels gibt es eine 52-Bit-Mantisse und einen 11-Bit-Exponenten. Lassen Sie es uns in Stücke brechen:
Die kleinste darstellbare Zahl größer als 1:
Deshalb:
Gibt es Zahlen zwischen 0 und epsilon? Viel ... ZB ist die minimale positiv darstellbare (normale) Zahl:
Tatsächlich gibt es
(1022 - 52 + 1)×2^52 = 4372995238176751616
Zahlen zwischen 0 und epsilon, was 47% aller positiv darstellbaren Zahlen entspricht ...quelle
0 <= e < 2048
wird die Mantisse mit 2 multipliziert mit der Potenz vone - 1023
. ZB wird der Exponent von2^0
alse=1023
,2^1
alse=1024
und2^-1022
als codierte=1
. Der Wert vone=0
ist für Subnormen und die reale Null reserviert.2^-1022
ist auch die kleinste normale Zahl. Die kleinste Zahl ist tatsächlich0.0000 00000000 00000000 00000000 00000000 00000000 00000001 × 2^-1022 = 2^-1074
. Dies ist subnormal, was bedeutet, dass der Mantissenteil kleiner als 1 ist und daher mit dem Exponenten codiert wirde=0
.Der Test ist sicherlich nicht der gleiche wie
someValue == 0
. Die ganze Idee von Gleitkommazahlen ist, dass sie einen Exponenten und einen Signifikanten speichern. Sie stellen daher einen Wert mit einer bestimmten Anzahl von binären signifikanten Genauigkeitszahlen dar (53 im Fall eines IEEE-Doppels). Die darstellbaren Werte sind nahe 0 viel dichter gepackt als nahe 1.Um ein vertrauteres Dezimalsystem zu verwenden, nehmen Sie an, Sie speichern einen Dezimalwert "bis 4 signifikante Zahlen" mit Exponent. Dann ist der nächste darstellbare Wert größer als
1
ist1.001 * 10^0
undepsilon
ist1.000 * 10^-3
. Ist1.000 * 10^-4
aber auch darstellbar, vorausgesetzt der Exponent kann -4 speichern. Sie können mein Wort dafür nehmen, dass ein IEEE-Double Exponenten speichern kann , die kleiner sind als der Exponent vonepsilon
.Anhand dieses Codes können Sie nicht allein erkennen, ob es sinnvoll ist, ihn
epsilon
speziell als Bindung zu verwenden. Sie müssen sich den Kontext ansehen. Es kann sein, dass diesepsilon
eine vernünftige Schätzung des Fehlers in der berechneten Berechnung istsomeValue
, und es kann sein, dass dies nicht der Fall ist.quelle
someValue == 0.0
oder nicht.Es gibt Zahlen zwischen 0 und epsilon, da epsilon die Differenz zwischen 1 und der nächsthöheren Zahl ist, die über 1 dargestellt werden kann, und nicht die Differenz zwischen 0 und der nächsthöheren Zahl, die über 0 dargestellt werden kann (wenn dies der Fall wäre) Code würde sehr wenig tun): -
Stoppen Sie das Programm mit einem Debugger am Ende von main und sehen Sie sich die Ergebnisse an. Sie werden feststellen, dass sich epsilon / 2 von epsilon, zero und one unterscheidet.
Diese Funktion nimmt also Werte zwischen +/- Epsilon an und macht sie zu Null.
quelle
Eine Annäherung von Epsilon (kleinstmöglicher Unterschied) um eine Zahl (1,0, 0,0, ...) kann mit dem folgenden Programm gedruckt werden. Es wird die folgende Ausgabe gedruckt:
epsilon for 0.0 is 4.940656e-324
epsilon for 1.0 is 2.220446e-16
Ein wenig Nachdenken macht deutlich, dass das Epsilon umso kleiner wird, je kleiner die Zahl ist, die wir für die Betrachtung seines Epsilon-Werts verwenden, da sich der Exponent an die Größe dieser Zahl anpassen kann.
quelle
Angenommen, wir arbeiten mit Spielzeug-Gleitkommazahlen, die in ein 16-Bit-Register passen. Es gibt ein Vorzeichenbit, einen 5-Bit-Exponenten und eine 10-Bit-Mantisse.
Der Wert dieser Gleitkommazahl ist die Mantisse, die als binärer Dezimalwert interpretiert wird, mal zwei hoch der Potenz des Exponenten.
Um 1 ist der Exponent gleich Null. Die kleinste Ziffer der Mantisse ist also ein Teil von 1024.
In der Nähe von 1/2 ist der Exponent minus eins, sodass der kleinste Teil der Mantisse halb so groß ist. Mit einem Fünf-Bit-Exponenten kann er negative 16 erreichen. An diesem Punkt ist der kleinste Teil der Mantisse einen Teil in 32 m wert. Und bei einem negativen 16-Exponenten liegt der Wert um einen Teil in 32k, viel näher an Null als das Epsilon um einen, den wir oben berechnet haben!
Dies ist ein Spielzeug-Gleitkommamodell, das nicht alle Macken eines echten Gleitkommasystems widerspiegelt, aber die Fähigkeit, Werte zu reflektieren, die kleiner als epsilon sind, ist mit echten Gleitkommawerten ziemlich ähnlich.
quelle
Die Differenz zwischen
X
und dem nächsten Wert vonX
variiert je nachX
.epsilon()
ist nur die Differenz zwischen1
und dem nächsten Wert von1
.Der Unterschied zwischen
0
und dem nächsten Wert von0
ist nichtepsilon()
.Stattdessen können Sie
std::nextafter
einen doppelten Wert0
wie folgt vergleichen:quelle
Ich denke, das hängt von der Präzision Ihres Computers ab. Schauen Sie sich diese Tabelle an : Sie können sehen, dass der Vergleich nicht gleichwertig ist, wenn Ihr Epsilon durch Doppel dargestellt wird, Ihre Genauigkeit jedoch höher ist
Gute Frage trotzdem!
quelle
Sie können dies aufgrund von Mantissen- und Exponententeilen nicht auf 0 anwenden. Aufgrund des Exponenten können Sie sehr kleine Zahlen speichern, die kleiner als epsilon sind. Wenn Sie jedoch versuchen, etwas wie (1.0 - "sehr kleine Zahl") zu tun, erhalten Sie 1.0. Epsilon ist ein Indikator nicht für den Wert, sondern für die Wertgenauigkeit, die in Mantisse angegeben ist. Es zeigt, wie viele korrekte nachfolgende Dezimalstellen der Zahl wir speichern können.
quelle
Beim IEEE-Gleitkomma gibt es zwischen dem kleinsten positiven Wert ungleich Null und dem kleinsten negativen Wert ungleich Null zwei Werte: positive Null und negative Null. Das Testen, ob ein Wert zwischen den kleinsten Werten ungleich Null liegt, entspricht dem Testen der Gleichheit mit Null. Die Zuweisung kann jedoch Auswirkungen haben, da sie eine negative Null in eine positive Null ändern würde.
Es wäre denkbar, dass ein Gleitkommaformat drei Werte zwischen den kleinsten endlichen positiven und negativen Werten aufweist: positive infinitesimale, vorzeichenlose Null und negative infinitesimale. Ich kenne keine Gleitkommaformate, die tatsächlich so funktionieren, aber ein solches Verhalten wäre durchaus vernünftig und wohl besser als das von IEEE (vielleicht nicht besser genug, um zusätzliche Hardware zur Unterstützung hinzuzufügen, aber mathematisch 1 / (1 / INF), 1 / (- 1 / INF) und 1 / (1-1) sollten drei verschiedene Fälle darstellen, die drei verschiedene Nullen darstellen. Ich weiß nicht, ob ein C-Standard vorschreiben würde, dass signierte Infinitesimale, falls vorhanden, gleich Null sein müssten. Wenn dies nicht der Fall ist, könnte Code wie der oben genannte sicherstellen, dass z
quelle
Nehmen wir also an, das System kann 1.000000000000000000000 und 1.000000000000000000001 nicht unterscheiden. das ist 1,0 und 1,0 + 1e-20. Denken Sie, dass es noch einige Werte gibt, die zwischen -1e-20 und + 1e-20 dargestellt werden können?
quelle
epsilon
. Weil es Gleitkomma ist, nicht Fixpunkt.Auch ein guter Grund für eine solche Funktion ist auch das Entfernen von "Denormals" (jene sehr kleinen Zahlen, die die implizierte führende "1" nicht mehr verwenden können und eine spezielle FP-Darstellung haben). Warum willst du das tun? Weil einige Maschinen (insbesondere einige ältere Pentium 4) bei der Verarbeitung von Denormals sehr, sehr langsam werden. Andere werden nur etwas langsamer. Wenn Ihre Anwendung diese sehr kleinen Zahlen nicht wirklich benötigt, ist es eine gute Lösung, sie auf Null zu setzen. Gute Orte, um dies zu berücksichtigen, sind die letzten Schritte von IIR-Filtern oder Abklingfunktionen.
Siehe auch: Warum verlangsamt das Ändern von 0.1f auf 0 die Leistung um das 10-fache?
und http://en.wikipedia.org/wiki/Denormal_number
quelle