Saya tahu angka 0.1
desimal tidak dapat diwakili secara tepat dengan angka biner yang terbatas ( penjelasan ), sehingga double n = 0.1
akan kehilangan presisi dan tidak akan persis 0.1
. Di sisi lain 0.5
dapat diwakili tepat karena itu 0.5 = 1/2 = 0.1b
.
Setelah mengatakan bahwa dapat dimengerti bahwa menambahkan 0.1
tiga kali tidak akan memberikan dengan tepat 0.3
sehingga kode berikut dicetak false
:
double sum = 0, d = 0.1;
for (int i = 0; i < 3; i++)
sum += d;
System.out.println(sum == 0.3); // Prints false, OK
Tapi bagaimana mungkin menambahkan 0.1
lima kali akan memberi dengan tepat 0.5
? Kode berikut dicetak true
:
double sum = 0, d = 0.1;
for (int i = 0; i < 5; i++)
sum += d;
System.out.println(sum == 0.5); // Prints true, WHY?
Jika 0.1
tidak dapat direpresentasikan secara tepat, bagaimana cara menambahkannya 5 kali dengan tepat 0.5
yang dapat direpresentasikan dengan tepat?
sum
memiliki nilai akhir yang sama seperti jika loop benar-benar dieksekusi. Dalam standar C ++ ini disebut "as-jika aturan" atau "perilaku yang dapat diamati sama".Jawaban:
Kesalahan pembulatan tidak acak dan cara penerapannya berupaya meminimalkan kesalahan. Ini berarti bahwa kadang-kadang kesalahan tidak terlihat, atau tidak ada kesalahan.
Misalnya
0.1
tidak persis0.1
yaitunew BigDecimal("0.1") < new BigDecimal(0.1)
tetapi0.5
persis1.0/2
Program ini menunjukkan kepada Anda nilai-nilai sebenarnya yang terlibat.
cetakan
Catatan: itu
0.3
sedikit mati, tetapi ketika Anda mendapatkan0.4
bit harus bergeser ke bawah agar sesuai dengan batas 53-bit dan kesalahan dibuang. Sekali lagi, kesalahan merayap kembali untuk0.6
dan0.7
tetapi untuk0.8
ke1.0
kesalahan dibuang.Alasan ada kesalahan adalah karena presisi yang terbatas. yaitu 53-bit. Ini berarti bahwa ketika angka menggunakan bit lebih banyak karena semakin besar, bit harus dijatuhkan dari ujung. Ini menyebabkan pembulatan yang dalam hal ini menguntungkan Anda.
Anda bisa mendapatkan efek sebaliknya ketika mendapatkan angka yang lebih kecil, misalnya
0.1-0.0999
=>1.0000000000000286E-4
dan Anda melihat lebih banyak kesalahan daripada sebelumnya.Contoh dari ini adalah mengapa di Java 6 Mengapa Math.round (0.49999999999999994) kembali 1 Dalam hal ini hilangnya sedikit dalam perhitungan menghasilkan perbedaan besar untuk jawabannya.
sumber
strictfp
Waktu untuk mempertimbangkan integer titik tetap saya pikir. (atau BigDecimal)Pembatasan overflow, dalam floating-point,
x + x + x
adalah persis angka floating-point yang dibulatkan dengan benar (yaitu terdekat) dengan 3 * yang sebenarnyax
,x + x + x + x
tepat 4 *x
, danx + x + x + x + x
sekali lagi merupakan perkiraan titik mengambang yang dibulatkan dengan benar untuk 5 *x
.Hasil pertama, karena
x + x + x
, berasal dari fakta yangx + x
tepat.x + x + x
dengan demikian hasil dari hanya satu pembulatan.Hasil kedua lebih sulit, satu demonstrasi itu dibahas di sini (dan Stephen Canon menyinggung bukti lain dengan analisis kasus pada 3 digit terakhir
x
). Untuk meringkas, 3 *x
berada di binade yang sama dengan 2 *x
atau di binade yang sama dengan 4 *x
, dan dalam setiap kasus adalah mungkin untuk menyimpulkan bahwa kesalahan pada penambahan ketiga membatalkan kesalahan pada penambahan kedua ( Tambahan pertama adalah tepat, seperti yang sudah kami katakan).Hasil ketiga, "
x + x + x + x + x
dibulatkan dengan benar", berasal dari yang kedua dengan cara yang sama dengan yang pertama berasal dari ketepatanx + x
.Hasil kedua menjelaskan mengapa
0.1 + 0.1 + 0.1 + 0.1
persis angka floating-point0.4
: bilangan rasional 1/10 dan 4/10 dapat didekati dengan cara yang sama, dengan kesalahan relatif yang sama, ketika dikonversi ke floating-point. Angka floating-point ini memiliki rasio tepat 4 di antara mereka. Hasil pertama dan ketiga menunjukkan bahwa0.1 + 0.1 + 0.1
dan0.1 + 0.1 + 0.1 + 0.1 + 0.1
dapat diperkirakan memiliki lebih sedikit kesalahan daripada yang dapat disimpulkan dengan analisis kesalahan naif, tetapi, dalam diri mereka sendiri, mereka hanya mengaitkan hasilnya dengan masing3 * 0.1
- masing dan5 * 0.1
, yang dapat diharapkan dekat tetapi tidak selalu identik dengan0.3
dan0.5
.Jika Anda terus menambahkan
0.1
setelah penambahan keempat, Anda akhirnya akan mengamati kesalahan pembulatan yang membuat "0.1
ditambahkan ke dirinya sendiri n kali" menyimpang darin * 0.1
, dan bahkan menyimpang lebih dari n / 10. Jika Anda memplot nilai "0,1 ditambahkan ke dirinya sendiri n kali" sebagai fungsi dari n, Anda akan mengamati garis kemiringan konstan oleh binades (segera setelah hasil penambahan n ditakdirkan untuk jatuh ke dalam binade tertentu, sifat-sifat tambahan dapat diharapkan mirip dengan penambahan sebelumnya yang menghasilkan hasil dalam binade yang sama). Di dalam binade yang sama, kesalahan akan tumbuh atau menyusut. Jika Anda melihat urutan lereng dari binade ke binade, Anda akan mengenali angka berulang dari0.1
dalam biner untuk sementara waktu. Setelah itu, penyerapan akan mulai terjadi dan kurva akan mendatar.sumber
x + x + x
persis angka floating-point yang dibulatkan dengan benar ke 3 *x
. "Dibulatkan dengan benar" berarti "terdekat" dalam konteks ini.Sistem floating point melakukan berbagai keajaiban termasuk memiliki beberapa bit ekstra presisi untuk pembulatan. Jadi kesalahan yang sangat kecil karena representasi tidak tepat dari 0,1 akhirnya dibulatkan menjadi 0,5.
Pikirkan floating point sebagai cara yang hebat tetapi INEXACT untuk mewakili angka. Tidak semua angka yang mungkin dengan mudah direpresentasikan dalam komputer. Bilangan irasional seperti PI. Atau seperti SQRT (2). (Sistem matematika simbolik dapat mewakili mereka, tetapi saya memang mengatakan "dengan mudah".)
Nilai floating point mungkin sangat dekat, tetapi tidak tepat. Mungkin sangat dekat sehingga Anda dapat menavigasi ke Pluto dan pergi dengan milimeter. Tetapi masih belum tepat dalam arti matematika.
Jangan gunakan floating point saat Anda harus tepat daripada perkiraan. Misalnya, aplikasi akuntansi ingin melacak dengan tepat jumlah uang tertentu dalam akun. Integer baik untuk itu karena mereka tepat. Masalah utama yang perlu Anda perhatikan dengan bilangan bulat adalah overflow.
Menggunakan BigDecimal untuk mata uang berfungsi dengan baik karena representasi yang mendasarinya adalah bilangan bulat, meskipun besar.
Menyadari bahwa angka floating point tidak tepat, mereka masih memiliki banyak kegunaan. Koordinasikan sistem untuk navigasi atau koordinat dalam sistem grafis. Nilai-nilai astronomi. Nilai-nilai ilmiah. (Anda mungkin tidak bisa mengetahui massa pasti bola dalam massa elektron, jadi ketidaktepatannya tidak terlalu penting.)
Untuk menghitung aplikasi (termasuk akuntansi) gunakan integer. Untuk menghitung jumlah orang yang melewati gerbang, gunakan int atau panjang.
sumber
strictfp
). Hanya karena Anda telah menolak untuk memahami sesuatu tidak berarti itu tidak terduga atau bahwa orang lain harus menolak untuk memahaminya. Lihat stackoverflow.com/questions/18496560 sebagai contoh panjang implementasi Java untuk menerapkan definisi bahasa (yang tidak termasuk ketentuan untuk bit presisi ekstra atau, denganstrictfp
, untuk bit exp tambahan)