Numeric_limits Machine Epsilon neden 1 + e> 1 koşulunu karşılamıyor?

Dec 24 2020

Yanılmıyorsam Machine Epsilon'un tanımı, koşulu karşılayan en düşük sayıdır:

Bunu kullanarak test etmeye çalışıyordum, std::numeric_limits<float>::epsilon()ancak değer bunu tatmin etmiyor, eğer önceki kayan numarayı şu şekilde almaya çalışırsanız 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;
}

Bu fotoğraf çıktıları true https://coliru.stacked-crooked.com/a/841e19dafcf0bf6f.

Numarayı kullanarak almaya çalıştıktan sonra std::nextafter, uygun Machine Epsilon'un şöyle olması gerektiğini fark ettim:

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

Bu kodu kullanarak test ettim:

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

Bu çıktılar

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

Burada bir şey mi özlüyorum?

Yanıtlar

6 JaMiT Dec 24 2020 at 11:46

Yanılmıyorsam Machine Epsilon'un tanımı koşulu karşılayan en düşük sayıdır: [ 1 + epsilon > 1]

Kapat, ancak C ++ bağlamında yanılıyorsun. ( Tanımınızın diğer, daha akademik bağlamlarda doğru olduğuna inanıyorum.) Cppreference.com'a göre , makine epsilon " 1.0[belirtilen] kayan nokta türü ile gösterilebilen bir sonraki değer arasındaki fark ve bir sonraki değerdir". Makine epsilon tatmin eder 1 + epsilon > 1, ancak bunu karşılayan en düşük sayı olması gerekmez . Bununla birlikte, tüm yuvarlama modlarında bu koşulu karşılayan en düşük sayıdır .

Makine epsilon'dan çok daha küçük olduğu için 1.0, epsilon ve 0.0. (Bu, kayan nokta gösterimlerinin temel amacıdır.) Bunlardan herhangi biri eklendiğinde 1.0, sonuç gösterilemez, bu nedenle sonucun yuvarlanması gerekir. Yuvarlama modu gösterilebilir en yakın değere ise, o zaman bu toplam 1 + epsilon, küçük sayı epsilon/2ve arasında olduğunda yuvarlanacaktır 3*epsilon/2. Öte yandan, yuvarlama modu her zaman sıfıra doğru ise, beklediğiniz sonucu alırsınız.

Kodunuza #include <cfenv>ve aşağıdaki satırı eklemeyi deneyin .

fesetround(FE_TOWARDZERO);

Bu, kesinlikle arasındaki 1.0ve 1 + epsilonyuvarlanan herhangi bir meblağa neden olur 1.0. Şimdi epsilon makinesinin beklediğiniz gibi davrandığını görmelisiniz.

Diğer garantili yuvarlama modları -sonsuza ve + sonsuza doğrudur. Ayrıntılar için cppreference.com adresine bakın.