Warum erfüllt die numeric_limits Machine Epsilon die Bedingung 1 + e> 1 nicht?
Wenn ich mich nicht irre, ist die Definition der Maschine Epsilon die niedrigste Zahl, die die Bedingung erfüllt:
Ich habe versucht, dies unter Verwendung von zu testen, std::numeric_limits<float>::epsilon()
aber der Wert ist nicht zufriedenstellend, wenn Sie versuchen, die vorherige Float-Nummer zu erhalten mit std::nextafter
:
#include <cmath>
#include <iostream>
#include <limits>
int main() {
float e = std::numeric_limits<float>::epsilon();
float previous = std::nextafter(e, -std::numeric_limits<float>::infinity());
std::cout << std::boolalpha << ((1.0f + previous) > 1.0f) << std::endl;
return 0;
}
Dadurch werden die Ausgänge stillgelegt true
https://coliru.stacked-crooked.com/a/841e19dafcf0bf6f.
Nachdem std::nextafter
ich versucht hatte, die Nummer mit zu ermitteln, stellte ich fest, dass die richtige Maschine Epsilon sein sollte:
std::nextafter(std::numeric_limits<float>::epsilon() / 2.0f, std::numeric_limits<float>::infinity())
Ich habe es mit diesem Code getestet:
#include <cmath>
#include <iostream>
#include <limits>
bool verify(float e) {
return ((1.0f + e) > 1.0f);
}
int main() {
std::cout.precision(std::numeric_limits<float>::digits);
std::cout << std::boolalpha << std::fixed;
float epsilon = std::numeric_limits<float>::epsilon();
float last = epsilon;
while (true) {
last = std::nextafter(last, -std::numeric_limits<float>::infinity());
if ((1.0f + last) > 1.0f) {
epsilon = last;
} else {
break;
}
}
// Does not satisfy condition
std::cout << "last: " << verify(last) << " " << last << std::endl;
// Satisfy condition
std::cout << "epsilon: " << verify(epsilon) << " " << epsilon << std::endl;
float half_epsilon = std::numeric_limits<float>::epsilon() / 2.0f;
float actual_epsilon = std::nextafter(half_epsilon, std::numeric_limits<float>::infinity());
// Same as 'last' at this point
std::cout << "half_epsilon: " << verify(half_epsilon) << " " << half_epsilon << std::endl;
// Same as 'epsilon' at this point
std::cout << "actual_epsilon: " << verify(actual_epsilon) << " " << actual_epsilon << std::endl;
return 0;
}
Dies gibt aus
last: false 0.000000059604644775390625
epsilon: true 0.000000059604651880817983
half_epsilon: false 0.000000059604644775390625
actual_epsilon: true 0.000000059604651880817983
https://coliru.stacked-crooked.com/a/3c66a2144e80a91b
Vermisse ich hier etwas?
Antworten
Wenn ich mich nicht irre, ist die Definition der Maschine Epsilon die niedrigste Zahl, die die Bedingung erfüllt: [
1 + epsilon > 1
]
Schließen, aber Sie sind im Kontext von C ++ falsch. (Ich glaube, Ihre Definition ist in anderen, akademischeren Kontexten korrekt.) Laut cppreference.com ist das Maschinen-Epsilon "die Differenz zwischen 1.0
und dem nächsten Wert, der durch den [angegebenen] Gleitkommatyp dargestellt werden kann". Das Maschinen-Epsilon erfüllt zwar 1 + epsilon > 1
, aber es muss nicht die niedrigste Zahl sein, die dies erfüllt. Es ist jedoch die niedrigste Zahl, die diese Bedingung in allen Rundungsmodi erfüllt .
Da das Maschinen-Epsilon so viel kleiner ist als 1.0
, gibt es viele darstellbare Werte zwischen dem Epsilon und 0.0
. ( 1.0
Dies ist ein grundlegendes Ziel von Gleitkomma-Darstellungen.) Wenn eines dieser Elemente hinzugefügt wird, ist das Ergebnis nicht darstellbar, daher muss das Ergebnis gerundet werden. Wenn der Rundungsmodus auf den nächsten darstellbaren Wert eingestellt ist, wird diese Summe immer dann gerundet, 1 + epsilon
wenn die kleine Zahl zwischen epsilon/2
und liegt 3*epsilon/2
. Wenn der Rundungsmodus dagegen immer gegen Null geht, erhalten Sie das erwartete Ergebnis.
Versuchen Sie #include <cfenv>
, Ihrem Code die folgende Zeile hinzuzufügen .
fesetround(FE_TOWARDZERO);
Dies führt dazu, dass jede Summe streng zwischen 1.0
und 1 + epsilon
gerundet wird 1.0
. Sie sollten jetzt sehen, wie sich das Maschinen-Epsilon wie erwartet verhält.
Die anderen garantierten Rundungsmodi sind gegen -Infinity und gegen + unendlich. Weitere Informationen finden Sie unter cppreference.com .