Warum erfüllt die numeric_limits Machine Epsilon die Bedingung 1 + e> 1 nicht?

Dec 24 2020

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::nextafterich 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

6 JaMiT Dec 24 2020 at 11:46

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.0und 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.0Dies 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 + epsilonwenn die kleine Zahl zwischen epsilon/2und 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.0und 1 + epsilongerundet 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 .