Hari ini, saya melihat-lihat beberapa kode C ++ (ditulis oleh orang lain) dan menemukan bagian ini:
double someValue = ...
if (someValue < std::numeric_limits<double>::epsilon() &&
someValue > -std::numeric_limits<double>::epsilon()) {
someValue = 0.0;
}
Saya mencoba mencari tahu apakah ini masuk akal.
Dokumentasi untuk epsilon()
mengatakan:
Fungsi mengembalikan selisih antara 1 dan nilai terkecil lebih besar dari 1 yang dapat direpresentasikan [oleh gandakan].
Apakah ini berlaku untuk 0 juga, yaitu epsilon()
apakah nilai terkecil lebih besar dari 0? Atau adakah angka antara 0
dan 0 + epsilon
yang dapat diwakili oleh a double
?
Jika tidak, bukankah perbandingannya setara dengan someValue == 0.0
?
numeric_limits<>::epsilon
menyesatkan dan tidak relevan. Yang kita inginkan adalah mengasumsikan 0 jika nilai aktual berbeda tidak lebih dari beberapa ε dari 0. Dan ε harus dipilih berdasarkan spesifikasi masalah, bukan pada nilai yang tergantung pada mesin. Saya menduga bahwa epsilon saat ini tidak berguna, karena bahkan hanya beberapa operasi FP dapat mengakumulasi kesalahan yang lebih besar dari itu.Jawaban:
Dengan asumsi 64-bit IEEE double, ada mantissa 52-bit dan eksponen 11-bit. Mari kita hancurkan menjadi bit:
Angka keterwakilan terkecil yang lebih besar dari 1:
Karena itu:
Apakah ada angka antara 0 dan epsilon? Banyak ... Misalnya angka minimum positif representable (normal) adalah:
Bahkan ada
(1022 - 52 + 1)×2^52 = 4372995238176751616
angka antara 0 dan epsilon, yang merupakan 47% dari semua angka positif yang dapat diwakili ...sumber
0 <= e < 2048
maka mantissa dikalikan 2 dengan kekuatane - 1023
. Misalnya eksponen2^0
dikodekan sebagaie=1023
,2^1
ase=1024
dan2^-1022
ase=1
. Nilaie=0
dicadangkan untuk subnormal dan nol nyata.2^-1022
merupakan angka normal terkecil . Jumlah terkecil sebenarnya0.0000 00000000 00000000 00000000 00000000 00000000 00000001 × 2^-1022 = 2^-1074
. Ini subnormal, artinya bagian mantissa lebih kecil dari 1, sehingga dikodekan dengan eksponene=0
.Tesnya tentu tidak sama dengan
someValue == 0
. Seluruh ide angka floating-point adalah bahwa mereka menyimpan eksponen dan signifikan. Oleh karena itu mereka mewakili nilai dengan angka presisi signifikan biner tertentu (53 dalam kasus ganda IEEE). Nilai yang diwakili jauh lebih padat di dekat 0 daripada di dekat 1.Untuk menggunakan sistem desimal yang lebih dikenal, misalkan Anda menyimpan nilai desimal "ke 4 angka penting" dengan eksponen. Maka selanjutnya representable nilai lebih besar dari
1
yang1.001 * 10^0
, danepsilon
adalah1.000 * 10^-3
. Tetapi1.000 * 10^-4
juga representable, dengan asumsi bahwa eksponen dapat menyimpan -4. Anda dapat mengambil kata saya untuk itu bahwa IEEE ganda dapat menyimpan eksponen kurang dari eksponenepsilon
.Anda tidak dapat mengetahui dari kode ini sendiri apakah masuk akal atau tidak menggunakan
epsilon
secara spesifik sebagai terikat, Anda perlu melihat konteksnya. Bisa jadi ituepsilon
adalah perkiraan yang masuk akal dari kesalahan dalam perhitungan yang dihasilkansomeValue
, dan mungkin itu bukan.sumber
someValue == 0.0
atau tidak.Ada angka yang ada antara 0 dan epsilon karena epsilon adalah perbedaan antara 1 dan angka tertinggi berikutnya yang dapat diwakili di atas 1 dan bukan perbedaan antara 0 dan angka tertinggi berikutnya yang dapat diwakili di atas 0 (jika itu, itu kode akan sangat sedikit): -
Dengan menggunakan debugger, hentikan program di akhir main dan lihat hasilnya dan Anda akan melihat bahwa epsilon / 2 berbeda dari epsilon, nol dan satu.
Jadi fungsi ini mengambil nilai antara +/- epsilon dan menjadikannya nol.
sumber
Sebuah aproximation dari epsilon (perbedaan sekecil mungkin) di sekitar angka (1.0, 0.0, ...) dapat dicetak dengan program berikut. Ini mencetak output berikut:
epsilon for 0.0 is 4.940656e-324
epsilon for 1.0 is 2.220446e-16
Sedikit pemikiran membuatnya jelas, bahwa epsilon semakin kecil semakin kecil jumlahnya yang kita gunakan untuk melihat nilai epsilon-nya, karena eksponen dapat menyesuaikan dengan ukuran angka itu.
sumber
Misalkan kita bekerja dengan angka floating point mainan yang sesuai dengan register 16 bit. Ada tanda bit, eksponen 5 bit, dan mantissa 10 bit.
Nilai angka floating point ini adalah mantissa, yang ditafsirkan sebagai nilai desimal biner, dua kali lipat dari pangkat eksponen.
Sekitar 1 eksponen sama dengan nol. Jadi digit terkecil dari mantissa adalah satu bagian dalam 1024.
Dekat 1/2 eksponen minus satu, sehingga bagian terkecil dari mantissa adalah setengah besar. Dengan eksponen lima bit dapat mencapai negatif 16, di mana titik terkecil mantissa bernilai satu bagian dalam 32m. Dan pada eksponen negatif 16, nilainya sekitar satu bagian dalam 32k, lebih dekat ke nol daripada epsilon di sekitar yang kita hitung di atas!
Sekarang ini adalah model floating point mainan yang tidak mencerminkan semua kebiasaan sistem floating point nyata, tetapi kemampuan untuk mencerminkan nilai yang lebih kecil dari epsilon cukup mirip dengan nilai floating point yang sebenarnya.
sumber
Perbedaan antara
X
dan nilai selanjutnyaX
bervariasi sesuai denganX
.epsilon()
hanya perbedaan antara1
dan nilai selanjutnya dari1
.Perbedaan antara
0
dan nilai selanjutnya0
adalah tidakepsilon()
.Sebagai gantinya Anda dapat menggunakan
std::nextafter
untuk membandingkan nilai ganda dengan0
sebagai berikut:sumber
Saya pikir itu tergantung pada ketepatan komputer Anda. Lihatlah tabel ini : Anda dapat melihat bahwa jika epsilon Anda diwakili oleh double, tetapi presisi Anda lebih tinggi, perbandingannya tidak setara dengan
Pertanyaan yang bagus!
sumber
Anda tidak dapat menerapkan ini ke 0, karena mantissa dan bagian eksponen. Karena eksponen Anda dapat menyimpan angka yang sangat kecil, yang lebih kecil dari epsilon, tetapi ketika Anda mencoba melakukan sesuatu seperti (1.0 - "angka sangat kecil") Anda akan mendapatkan 1,0. Epsilon adalah indikator bukan nilai, tetapi presisi nilai, yang ada di mantissa. Ini menunjukkan berapa banyak angka desimal yang benar yang dapat kita simpan.
sumber
Dengan IEEE floating-point, antara nilai positif non-nol terkecil dan nilai negatif non-nol terkecil, terdapat dua nilai: nol positif dan nol negatif. Menguji apakah suatu nilai berada di antara nilai non-nol terkecil sama dengan menguji kesetaraan dengan nol; penugasan, bagaimanapun, dapat memiliki efek, karena itu akan mengubah nol negatif menjadi nol positif.
Dapat dibayangkan bahwa format floating-point mungkin memiliki tiga nilai antara nilai positif dan negatif hingga terbatas terkecil: positif sangat kecil, nol tidak ditandatangani, dan negatif sangat kecil. Saya tidak terbiasa dengan format floating-point yang sebenarnya bekerja seperti itu, tetapi perilaku seperti itu akan sangat masuk akal dan bisa dibilang lebih baik daripada IEEE (mungkin tidak cukup baik untuk menambahkan perangkat keras tambahan untuk mendukungnya, tetapi secara matematis 1 / (1 / INF), 1 / (- 1 / INF), dan 1 / (1-1) harus mewakili tiga kasus berbeda yang menggambarkan tiga nol berbeda). Saya tidak tahu apakah ada standar C yang akan mengamanatkan bahwa infinitesimal yang ditandatangani, jika ada, harus dibandingkan dengan nol. Jika tidak, kode seperti di atas dapat bermanfaat misalnya
sumber
Jadi katakanlah sistem tidak dapat membedakan 1,000000000000000000000 dan 1,00000000000000000000001. yaitu 1.0 dan 1.0 + 1e-20. Apakah Anda pikir masih ada beberapa nilai yang dapat direpresentasikan antara -1e-20 dan + 1e-20?
sumber
epsilon
. Karena itu titik mengambang , bukan titik tetap.Juga, alasan yang baik untuk memiliki fungsi seperti itu adalah untuk menghapus "denormals" (angka-angka yang sangat kecil yang tidak dapat lagi menggunakan terkemuka yang tersirat "1" dan memiliki representasi FP khusus). Mengapa Anda ingin melakukan ini? Karena beberapa mesin (khususnya, beberapa Pentium 4 yang lebih tua) menjadi sangat, sangat lambat saat memproses denormals. Lainnya hanya sedikit lebih lambat. Jika aplikasi Anda tidak benar-benar membutuhkan angka yang sangat kecil ini, membilasnya ke nol adalah solusi yang baik. Tempat yang baik untuk mempertimbangkan ini adalah langkah terakhir dari setiap filter IIR atau fungsi peluruhan.
Lihat juga: Mengapa mengubah 0.1f ke 0 memperlambat kinerja sebesar 10x?
dan http://en.wikipedia.org/wiki/Denormal_number
sumber