Mengapa numeric_limits Machine Epsilon tidak memenuhi kondisi 1 + e> 1?
Jika saya tidak salah definisi Mesin Epsilon adalah angka terendah yang memenuhi kondisi:
Saya mencoba menguji ini dengan menggunakan std::numeric_limits<float>::epsilon()
tetapi nilainya tidak memuaskan ini, jika Anda mencoba mendapatkan nomor float sebelumnya dengan 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;
}
Ini masih menghasilkan keluaran true
https://coliru.stacked-crooked.com/a/841e19dafcf0bf6f.
Setelah mencoba mendapatkan nomor menggunakan std::nextafter
saya perhatikan bahwa Mesin Epsilon yang tepat harus:
std::nextafter(std::numeric_limits<float>::epsilon() / 2.0f, std::numeric_limits<float>::infinity())
Saya mengujinya menggunakan kode ini:
#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;
}
Output ini
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
Apakah saya melewatkan sesuatu di sini?
Jawaban
Jika saya tidak salah definisi Mesin Epsilon adalah angka terendah yang memenuhi kondisi: [
1 + epsilon > 1
]
Tutup, tetapi Anda salah dalam konteks C ++. (Saya yakin definisi Anda benar dalam konteks lain yang lebih akademis.) Menurut cppreference.com , mesin epsilon adalah "perbedaan antara 1.0
dan nilai berikutnya yang dapat direpresentasikan oleh tipe floating-point [ditentukan]". Mesin epsilon memang memuaskan 1 + epsilon > 1
, tetapi tidak perlu angka terendah yang memuaskan itu. Namun, ini adalah angka terendah yang memenuhi kondisi tersebut di bawah semua mode pembulatan .
Karena mesin epsilon jauh lebih kecil daripada 1.0
, ada banyak nilai yang dapat direpresentasikan antara epsilon dan 0.0
. (Itu adalah tujuan dasar representasi floating point.) Ketika salah satu dari ini ditambahkan 1.0
, hasilnya tidak dapat direpresentasikan, jadi hasilnya perlu dibulatkan. Jika mode pembulatan adalah ke nilai terwakili terdekat, maka jumlah itu akan dibulatkan ke 1 + epsilon
setiap kali angka kecil berada di antara epsilon/2
dan 3*epsilon/2
. Di sisi lain, jika mode pembulatan selalu menuju nol, maka Anda mendapatkan hasil yang Anda harapkan.
Coba tambahkan #include <cfenv>
dan baris berikut ke kode Anda.
fesetround(FE_TOWARDZERO);
Hal ini menyebabkan penjumlahan ketat antara 1.0
dan 1 + epsilon
untuk dibulatkan 1.0
. Anda sekarang harus melihat mesin epsilon berperilaku seperti yang Anda harapkan.
Mode pembulatan terjamin lainnya menuju -infinity dan menuju + infinity. Lihat cppreference.com untuk detailnya.