Numeric_limits Machine Epsilon neden 1 + e> 1 koşulunu karşılamıyor?
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
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/2
ve 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.0
ve 1 + epsilon
yuvarlanan 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.