Apakah mungkin mendapatkan pembagian dengan 0 (atau tak terhingga) dalam contoh berikut?
public double calculation(double a, double b)
{
if (a == b)
{
return 0;
}
else
{
return 2 / (a - b);
}
}
Dalam kasus normal, tentu saja tidak. Tetapi bagaimana jika a
dan b
sangat dekat, dapat (a-b)
menyebabkan 0
ketepatan perhitungan?
Perhatikan bahwa pertanyaan ini untuk Java, tapi saya pikir itu akan berlaku untuk sebagian besar bahasa pemrograman.
Jawaban:
Di Jawa,
a - b
tidak pernah sama dengan0
jikaa != b
. Ini karena Java mengamanatkan operasi floating point IEEE 754 yang mendukung angka denormalized. Dari spec :Jika FPU bekerja dengan angka yang didenormalkan , mengurangi angka yang tidak sama tidak akan pernah menghasilkan nol (tidak seperti penggandaan), lihat juga pertanyaan ini .
Untuk bahasa lain, itu tergantung. Dalam C atau C ++, misalnya, dukungan IEEE 754 adalah opsional.
Yang mengatakan, adalah mungkin untuk ekspresi
2 / (a - b)
meluap, misalnya dengana = 5e-308
danb = 4e-308
.sumber
(a,b) = (3,1)
=>2/(a-b) = 2/(3-1) = 2/2 = 1
Apakah ini benar dengan IEEE floating point, saya tidak tahuSebagai solusinya, bagaimana dengan yang berikut ini?
Dengan begitu Anda tidak bergantung pada dukungan IEEE dalam bahasa apa pun.
sumber
a=b
, Anda tidak boleh kembali0
. Membagi dengan0
di IEEE 754 membuat Anda tak terbatas, tidak terkecuali. Anda menghindari masalah, jadi kembali0
adalah bug yang menunggu untuk terjadi. Pertimbangkan1/x + 1
. Jikax=0
, itu menghasilkan1
, bukan nilai yang benar: infinity.0
tidak benar-benar masalah. Inilah yang dilakukan OP dalam pertanyaan. Anda bisa meletakkan pengecualian atau apa pun yang sesuai untuk situasi di bagian blok itu. Jika Anda tidak suka kembali0
, itu seharusnya kritik terhadap pertanyaan itu. Tentu saja, melakukan seperti yang dilakukan OP tidak menjamin downvote untuk jawabannya. Pertanyaan ini tidak ada hubungannya dengan perhitungan lebih lanjut setelah fungsi yang diberikan selesai. Untuk semua yang Anda tahu, persyaratan program harus dikembalikan0
.Anda tidak akan mendapatkan pembagian dengan nol terlepas dari nilainya
a - b
, karena pembagian floating point dengan 0 tidak menghasilkan pengecualian. Ia mengembalikan tak terhingga.Sekarang, satu-satunya cara
a == b
mengembalikan true adalah jikaa
danb
mengandung bit yang sama persis. Jika mereka berbeda sedikit saja, perbedaan di antara mereka tidak akan menjadi 0.EDIT:
Seperti yang dikatakan Batsyeba dengan benar, ada beberapa pengecualian:
"Bukan angka yang membandingkan" salah dengan dirinya sendiri tetapi akan memiliki pola bit yang identik.
-0.0 didefinisikan untuk membandingkan true dengan +0.0, dan pola bit mereka berbeda.
Jadi jika keduanya
a
danb
iniDouble.NaN
, Anda akan mencapai klausa lain, tetapi karenaNaN - NaN
juga kembaliNaN
, Anda tidak akan membaginya dengan nol.sumber
Tidak ada kasus di mana pembagian dengan nol dapat terjadi di sini.
The SMT Solver Z3 mendukung IEEE tepat aritmatika floating point. Mari kita minta Z3 untuk menemukan angka
a
danb
sedemikian rupa sehinggaa != b && (a - b) == 0
:Hasilnya adalah
UNSAT
. Tidak ada angka seperti itu.String SMTLIB di atas juga memungkinkan Z3 untuk memilih mode pembulatan sewenang-wenang (
rm
). Ini berarti bahwa hasilnya berlaku untuk semua mode pembulatan yang mungkin (yang ada lima). Hasilnya juga mencakup kemungkinan bahwa salah satu variabel dalam permainan mungkinNaN
atau tidak terbatas.a == b
diimplementasikan sebagaifp.eq
kualitas sehingga+0f
dan-0f
membandingkan sama. Perbandingan dengan nol diimplementasikan menggunakanfp.eq
juga. Karena pertanyaannya ditujukan untuk menghindari pembagian dengan nol, ini adalah perbandingan yang tepat.Jika tes kesetaraan diimplementasikan menggunakan persamaan bitwise,
+0f
dan-0f
akan menjadi cara untuk membuata - b
nol. Versi sebelumnya yang salah dari jawaban ini berisi detail mode tentang kasus itu untuk yang penasaran.Z3 Online belum mendukung teori FPA. Hasil ini diperoleh dengan menggunakan cabang tidak stabil terbaru. Itu dapat direproduksi menggunakan .NET bindings sebagai berikut:
Menggunakan Z3 untuk menjawab pertanyaan mengambang IEEE bagus karena sulit untuk mengabaikan kasus-kasus (seperti
NaN
,-0f
,+-inf
) dan Anda dapat mengajukan pertanyaan yang sewenang-wenang. Tidak perlu menafsirkan dan mengutip spesifikasi. Anda bahkan dapat mengajukan pertanyaan campuran mengambang dan bilangan bulat seperti "apakahint log2(float)
algoritma khusus ini benar?".sumber
Fungsi yang disediakan memang dapat mengembalikan tak terhingga:
Outputnya adalah
Result: -Infinity
.Ketika hasil pembagian adalah besar untuk disimpan dalam dobel, infinity dikembalikan bahkan jika penyebutnya bukan nol.
sumber
Dalam implementasi floating-point yang sesuai dengan IEEE-754, setiap tipe floating-point dapat menyimpan angka dalam dua format. Satu ("dinormalisasi") digunakan untuk sebagian besar nilai floating-point, tetapi angka terkecil kedua yang dapat diwakilinya hanya sedikit lebih besar dari yang terkecil, sehingga perbedaan di antara keduanya tidak dapat diwakili dalam format yang sama. Format lain ("dinormalkan") hanya digunakan untuk angka yang sangat kecil yang tidak dapat diwakili dalam format pertama.
Sirkuit untuk menangani format floating-point yang didenormalkan secara efisien mahal, dan tidak semua prosesor memasukkannya. Beberapa prosesor menawarkan pilihan antara memiliki operasi pada angka yang sangat kecil jauh lebih lambat daripada operasi pada nilai-nilai lain, atau meminta prosesor menganggap angka yang terlalu kecil untuk format yang dinormalisasi menjadi nol.
Spesifikasi Java menyiratkan bahwa implementasi harus mendukung format denormalized, bahkan pada mesin yang melakukannya akan membuat kode berjalan lebih lambat. Di sisi lain, ada kemungkinan bahwa beberapa implementasi mungkin menawarkan opsi untuk memungkinkan kode berjalan lebih cepat dengan imbalan penanganan nilai yang sedikit ceroboh yang untuk sebagian besar tujuan terlalu kecil untuk diperhitungkan (dalam kasus di mana nilai terlalu kecil untuk diperhitungkan, itu dapat menjengkelkan memiliki perhitungan dengan mereka membutuhkan waktu sepuluh kali selama perhitungan itu penting, sehingga dalam banyak situasi praktis flush-to-zero lebih berguna daripada aritmatika lambat tapi akurat).
sumber
Dahulu sebelum IEEE 754, sangat mungkin bahwa a! = B tidak menyiratkan ab! = 0 dan sebaliknya. Itu adalah salah satu alasan untuk membuat IEEE 754 di tempat pertama.
Dengan IEEE 754 hampir dijamin. Kompiler C atau C ++ diizinkan untuk melakukan operasi dengan presisi lebih tinggi dari yang dibutuhkan. Jadi jika a dan b bukan variabel tetapi ekspresi, maka (a + b)! = C tidak menyiratkan (a + b) - c! = 0, karena a + b dapat dihitung sekali dengan presisi lebih tinggi, dan sekali tanpa presisi yang lebih tinggi.
Banyak FPU dapat dialihkan ke mode di mana mereka tidak mengembalikan angka dinormalkan tetapi ganti dengan 0. Dalam mode itu, jika a dan b adalah angka normal kecil di mana perbedaannya lebih kecil dari angka normalisasi terkecil tetapi lebih besar dari 0, a ! = b juga tidak menjamin a == b.
"Jangan pernah membandingkan angka titik apung" adalah pemrograman pemujaan kargo. Di antara orang-orang yang memiliki mantra "Anda membutuhkan epsilon", sebagian besar tidak tahu bagaimana memilih epsilon dengan benar.
sumber
Saya dapat memikirkan sebuah kasus di mana Anda mungkin dapat menyebabkan ini terjadi. Berikut adalah sampel analog pada basis 10 - sungguh, ini akan terjadi pada basis 2, tentu saja.
Angka titik apung disimpan lebih atau kurang dalam notasi ilmiah - yaitu, alih-alih melihat 35.2, angka yang disimpan akan lebih seperti 3.52e2.
Bayangkan demi kenyamanan bahwa kita memiliki unit floating point yang beroperasi di basis 10 dan memiliki 3 digit akurasi. Apa yang terjadi ketika Anda mengurangi 9,99 dari 10,0?
1,00e2-9,99e1
Shift untuk memberi masing-masing nilai eksponen yang sama
1,00e2-0,999e2
Membulatkan menjadi 3 digit
1,00e2-1,00e2
Uh oh!
Apakah ini bisa terjadi pada akhirnya tergantung pada desain FPU. Karena kisaran eksponen untuk double sangat besar, perangkat keras harus membulatkannya secara internal pada beberapa titik, tetapi dalam kasus di atas, hanya 1 digit tambahan secara internal akan mencegah masalah.
sumber
strictfp
tidak diaktifkan, mungkin saja perhitungan menghasilkan nilai yang terlalu kecil untukdouble
tetapi akan cocok dengan nilai floating-point presisi yang diperluas.strictfp
hanya memengaruhi nilai "hasil antara", dan saya mengutip dari docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.4 .a
danb
merupakandouble
variabel, bukan hasil antara, jadi nilainya adalah nilai presisi ganda, sehingga merupakan kelipatan 2 ^ -1074. Pengurangan dua nilai presisi ganda ini adalah kelipatan dari 2 ^ -1074, sehingga rentang eksponen yang lebih luas mengubah properti bahwa selisihnya adalah 0 iff a == b.Anda seharusnya tidak pernah membandingkan pelampung atau dobel untuk kesetaraan; karena, Anda tidak dapat benar-benar menjamin bahwa nomor yang Anda tetapkan untuk float atau dobel adalah tepat.
Untuk membandingkan mengapung untuk persamaan, Anda perlu memeriksa apakah nilainya "cukup dekat" dengan nilai yang sama:
sumber
abs(first - second) < error
(atau<= error
) lebih mudah dan lebih ringkas.Pembagian dengan nol tidak ditentukan, karena batas dari bilangan positif cenderung hingga tak terbatas, terbatas dari bilangan negatif cenderung tak hingga negatif.
Tidak yakin apakah ini C ++ atau Java karena tidak ada tag bahasa.
sumber
Masalah intinya adalah bahwa representasi komputer dari ganda (alias float, atau bilangan real dalam bahasa matematika) salah ketika Anda memiliki "terlalu banyak" desimal, misalnya ketika Anda berurusan dengan ganda yang tidak dapat ditulis sebagai nilai numerik ( pi atau hasil 1/3).
Jadi a == b tidak dapat dilakukan dengan nilai ganda a dan b, bagaimana Anda berurusan dengan a == b ketika a = 0,333 dan b = 1/3? Bergantung pada OS Anda vs FPU vs angka vs bahasa versus hitungan 3 setelah 0, Anda akan memiliki benar atau salah.
Pokoknya jika Anda melakukan "perhitungan nilai ganda" pada komputer, Anda harus berurusan dengan akurasi, jadi alih-alih melakukan
a==b
, Anda harus melakukannyaabsolute_value(a-b)<epsilon
, dan epsilon relatif terhadap apa yang Anda modelkan pada waktu itu dalam algoritma Anda. Anda tidak dapat memiliki nilai epsilon untuk semua perbandingan ganda Anda.Singkatnya, ketika Anda mengetik a == b, Anda memiliki ekspresi matematika yang tidak dapat diterjemahkan pada komputer (untuk setiap angka floating point).
PS: hum, semua yang saya jawab di sini kurang lebih dalam tanggapan dan komentar orang lain.
sumber
Berdasarkan tanggapan @malarres dan komentar @Taemyr, inilah kontribusi kecil saya:
Maksud saya adalah mengatakan: cara termudah untuk mengetahui apakah hasil pembagian adalah nan atau inf sebenarnya untuk melakukan pembagian.
sumber