Dlaczego numeric_limits Machine Epsilon nie spełnia warunku 1 + e> 1?

Dec 24 2020

Jeśli się nie mylę, definicja Machine Epsilon jest najniższą liczbą spełniającą warunek:

Próbowałem to przetestować przy użyciu the, std::numeric_limits<float>::epsilon()ale wartość nie jest satysfakcjonująca, jeśli spróbujesz uzyskać poprzednią liczbę zmiennoprzecinkową za pomocą 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;
}

To nadal powoduje, że wyjścia true https://coliru.stacked-crooked.com/a/841e19dafcf0bf6f.

Po próbie uzyskania numeru za pomocą std::nextafterzauważyłem, że właściwy Machine Epsilon powinien być:

std::nextafter(std::numeric_limits<float>::epsilon() / 2.0f, std::numeric_limits<float>::infinity())

Przetestowałem to za pomocą tego kodu:

#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;
}

To wyprowadza

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

Czy coś mi tu brakuje?

Odpowiedzi

6 JaMiT Dec 24 2020 at 11:46

Jeśli się nie mylę, definicja Machine Epsilon jest najniższą liczbą spełniającą warunek: [ 1 + epsilon > 1]

Blisko, ale mylisz się w kontekście C ++. (Uważam, że twoja definicja jest poprawna w innych, bardziej akademickich kontekstach.) Według cppreference.com , maszyna epsilon jest „różnicą między 1.0i następną wartością reprezentowaną przez [określony] typ zmiennoprzecinkowy”. Maszyna epsilon spełnia 1 + epsilon > 1wymagania, ale nie musi to być najniższa liczba, która to spełnia. Jest to jednak najniższa liczba, która spełnia ten warunek we wszystkich trybach zaokrąglania .

Ponieważ epsilon maszyny jest o wiele mniejsza niż 1.0, między epsilon i 0.0. (To jest podstawowy cel reprezentacji zmiennoprzecinkowych). Kiedy którekolwiek z nich jest dodawane 1.0, wynik nie jest reprezentowalny, więc wynik należy zaokrąglić. Jeśli tryb zaokrąglania jest zbliżony do najbliższej możliwej do reprezentacji wartości, suma ta zostanie zaokrąglona do 1 + epsilonilorazu małej liczby mieszczącej się w przedziale od epsilon/2do 3*epsilon/2. Z drugiej strony, jeśli tryb zaokrąglania jest zawsze zbliżony do zera, otrzymasz oczekiwany wynik.

Spróbuj dodać #include <cfenv>następujący wiersz do swojego kodu.

fesetround(FE_TOWARDZERO);

Powoduje to, że każda suma ściśle między 1.0i 1 + epsilonzaokrągla do 1.0. Powinieneś teraz zobaczyć, jak epsilon maszyny zachowuje się zgodnie z oczekiwaniami.

Inne gwarantowane tryby zaokrąglania to w kierunku -infinity i w kierunku + nieskończoności. Szczegółowe informacje można znaleźć na stronie cppreference.com .