Menurut halaman java.sun ini ==
adalah operator perbandingan kesetaraan untuk angka floating point di Jawa.
Namun, ketika saya mengetikkan kode ini:
if(sectionID == currentSectionID)
ke editor saya dan menjalankan analisis statis, saya mendapatkan: "Nilai titik mengambang JAVA0078 dibandingkan dengan =="
Apa yang salah dengan menggunakan ==
untuk membandingkan nilai floating point? Apa cara yang benar untuk melakukannya?
java
equality
floating-accuracy
pengguna128807
sumber
sumber
Jawaban:
cara yang benar untuk menguji mengapung untuk 'kesetaraan' adalah:
di mana epsilon adalah angka yang sangat kecil seperti 0,00000001, tergantung pada presisi yang diinginkan.
sumber
if(Math.abs(sectionID - currentSectionID) < epsilon*sectionID
untuk mengatasi masalah itu?Math.ulp()
jawaban saya untuk pertanyaan ini.Nilai floating point dapat dimatikan sedikit, sehingga nilai tersebut mungkin tidak sama persis. Misalnya, mengatur pelampung ke "6.1" dan kemudian mencetaknya lagi, Anda mungkin mendapatkan nilai yang dilaporkan seperti "6.099999904632568359375". Ini adalah dasar cara mengapung bekerja; Oleh karena itu, Anda tidak ingin membandingkannya menggunakan persamaan, melainkan perbandingan dalam rentang, yaitu, jika perbedaan float ke angka yang ingin Anda bandingkan dengan kurang dari nilai absolut tertentu.
Ini artikel di Register memberikan gambaran yang baik tentang mengapa hal ini terjadi; bacaan yang bermanfaat dan menarik.
sumber
Hanya untuk memberikan alasan di balik apa yang orang lain katakan.
Representasi biner dari float agak mengganggu.
Dalam biner, sebagian besar programmer mengetahui korelasi antara 1b = 1d, 10b = 2d, 100b = 4d, 1000b = 8d
Yah itu bekerja sebaliknya juga.
.1b = .5d, .01b = .25d, .001b = .125, ...
Masalahnya adalah bahwa tidak ada cara yang tepat untuk mewakili angka paling desimal seperti .1, .2, .3, dll. Yang dapat Anda lakukan hanyalah perkiraan dalam biner. Sistem melakukan pembulatan sedikit fudge ketika angka-angka dicetak sehingga menampilkan .1 bukannya .10000000000001 atau .999999999999 (yang mungkin sama sedekat mungkin dengan representasi yang disimpan seperti .1)
Sunting dari komentar: Alasan ini masalah adalah harapan kami. Kami sepenuhnya berharap 2/3 untuk dikaburkan pada titik tertentu ketika kami mengonversinya menjadi desimal, baik .7 atau .67 atau .666667 .. Tetapi kami tidak secara otomatis berharap .1 akan dibulatkan dengan cara yang sama seperti 2/3 --dan itulah yang terjadi.
Ngomong-ngomong, jika Anda penasaran nomor yang disimpan secara internal adalah representasi biner murni menggunakan "Notasi Ilmiah" biner. Jadi jika Anda mengatakan untuk menyimpan angka desimal 10.75d, itu akan menyimpan 1010b untuk angka 10, dan .11b untuk angka desimal. Jadi itu akan menyimpan 0,101011 kemudian menyimpan beberapa bit pada akhirnya untuk mengatakan: Pindahkan titik desimal empat tempat ke kanan.
(Meskipun secara teknis ini bukan lagi titik desimal, sekarang menjadi titik biner, tetapi terminologi itu tidak akan membuat hal-hal lebih dimengerti bagi kebanyakan orang yang akan menemukan jawaban ini dari penggunaan apa pun.)
sumber
Karena itu tidak benar
0.1 + 0.2 == 0.3
sumber
Float.compare(0.1f+0.2f, 0.3f) == 0
?Saya pikir ada banyak kebingungan di sekitar mengapung (dan ganda), ada baiknya untuk membersihkannya.
Tidak ada yang salah secara inheren dalam menggunakan float sebagai ID di JVM yang sesuai standar [*]. Jika Anda cukup mengatur float ID ke x, jangan lakukan apa-apa dengan itu (yaitu tidak ada aritmatika) dan kemudian tes untuk y == x, Anda akan baik-baik saja. Juga tidak ada yang salah dalam menggunakannya sebagai kunci dalam HashMap. Apa yang tidak dapat Anda lakukan adalah mengasumsikan persamaan
x == (x - y) + y
, dll. Karena ini, orang biasanya menggunakan tipe bilangan bulat sebagai ID, dan Anda dapat mengamati bahwa sebagian besar orang di sini ditunda oleh kode ini, jadi untuk alasan praktis, lebih baik mematuhi konvensi . Perhatikan bahwa ada banyakdouble
nilai yang berbeda dengan yang lamavalues
, jadi Anda tidak mendapatkan apa pun dengan menggunakandouble
. Juga, menghasilkan "ID tersedia berikutnya" bisa rumit dengan ganda dan membutuhkan pengetahuan tentang aritmatika titik-mengambang. Tidak sepadan dengan masalahnya.Di sisi lain, mengandalkan kesetaraan numerik dari hasil dua perhitungan yang setara secara matematis adalah berisiko. Ini karena kesalahan pembulatan dan kehilangan presisi ketika mengkonversi dari representasi desimal ke biner. Ini telah dibahas sampai mati pada SO.
[*] Ketika saya mengatakan "JVM yang sesuai standar" saya ingin mengecualikan implementasi JVM yang rusak otak. Lihat ini .
sumber
==
daripadaequals
, atau yang lain memastikan bahwa tidak ada float yang membandingkan tidak sama dengan dirinya sendiri akan disimpan dalam sebuah tabel. Jika tidak, sebuah program yang mencoba menghitung misalnya berapa hasil unik yang dapat dihasilkan dari ekspresi ketika diberi makan berbagai input dapat menganggap setiap nilai NaN sebagai unik.Float
, bukan untukfloat
.Float
? Jika seseorang mencoba membangun tabelfloat
nilai unik dan membandingkannya dengan==
, aturan perbandingan IEEE-754 yang mengerikan akan menghasilkan tabel yang dibanjiri denganNaN
nilai.float
tipe tidak memilikiequals
metode.equals
metode contoh, tetapi metode statis (saya pikir di dalamFloat
kelas) yang membandingkan dua nilai tipefloat
.Ini adalah masalah yang tidak spesifik untuk java. Menggunakan == untuk membandingkan dua float / ganda / angka desimal apa pun berpotensi menyebabkan masalah karena cara mereka disimpan. Pelampung presisi tunggal (sesuai standar IEEE 754) memiliki 32 bit, didistribusikan sebagai berikut:
1 bit - Masuk (0 = positif, 1 = negatif)
8 bit - Eksponen (khusus (bias-127) representasi x dalam 2 ^ x)
23 bit - Mantisa. Angka aktual yang disimpan.
Mantera inilah yang menyebabkan masalah. Ini agak seperti notasi ilmiah, hanya angka dalam basis 2 (biner) yang terlihat seperti 1,110011 x 2 ^ 5 atau yang serupa. Namun dalam biner, 1 pertama selalu 1 (kecuali untuk representasi 0)
Oleh karena itu, untuk menghemat sedikit ruang memori (pun intended), IEEE memutuskan bahwa 1 harus diasumsikan. Misalnya, mantisa 1011 benar-benar adalah 1,1011.
Ini dapat menyebabkan beberapa masalah dengan perbandingan, terutama dengan 0 karena 0 tidak mungkin diwakili secara tepat dalam float. Ini adalah alasan utama == tidak disarankan, di samping masalah matematika floating point yang dijelaskan oleh jawaban lain.
Java memiliki masalah unik karena bahasanya universal di banyak platform berbeda, yang masing-masing bisa memiliki format float unik itu sendiri. Itu membuatnya lebih penting untuk menghindari ==.
Cara yang tepat untuk membandingkan dua pelampung (bukan bahasa khusus Anda) untuk kesetaraan adalah sebagai berikut:
di mana ACCEPTABLE_ERROR adalah #defined atau konstanta lainnya sama dengan 0,000000001 atau presisi apa pun yang diperlukan, seperti yang sudah disebutkan Victor.
Beberapa bahasa memiliki fungsi ini atau konstanta bawaan ini, tetapi umumnya ini adalah kebiasaan yang baik untuk dilakukan.
sumber
Sampai hari ini, cara cepat & mudah untuk melakukannya adalah:
Namun, dokumen tidak secara jelas menentukan nilai selisih margin (sebuah epsilon dari jawaban @Victor) yang selalu ada dalam perhitungan pada float, tetapi harus menjadi sesuatu yang masuk akal karena merupakan bagian dari perpustakaan bahasa standar.
Namun jika dibutuhkan ketelitian yang lebih tinggi atau disesuaikan
adalah pilihan solusi lain.
sumber
(sectionId == currentSectionId)
yang tidak akurat untuk floating point. metode epsilon adalah pendekatan yang lebih baik, yang ada di jawaban ini: stackoverflow.com/a/1088271/4212710Nilai titik berbusa tidak dapat diandalkan, karena kesalahan pembulatan.
Karena itu, mereka mungkin tidak boleh digunakan sebagai nilai kunci, seperti sectionID. Gunakan bilangan bulat sebagai gantinya, atau
long
jikaint
tidak mengandung nilai yang cukup mungkin.sumber
double
jauh lebih tepat, tetapi mereka juga nilai floating point, jadi jawaban saya dimaksudkan untuk menyertakan keduanyafloat
dandouble
.Selain jawaban sebelumnya, Anda harus menyadari bahwa ada perilaku aneh yang terkait dengan
-0.0f
dan+0.0f
(mereka==
tetapi tidakequals
) danFloat.NaN
(ituequals
tapi tidak==
) (harapan saya benar - argh, jangan lakukan itu!).Sunting: Mari kita periksa!
Selamat datang di IEEE / 754.
sumber
==
tidak berarti angka "identik dengan bit" (angka yang sama dapat diwakili dengan pola bit yang berbeda, meskipun hanya satu dari mereka yang dinormalisasi bentuk). Juga,-0.0f
dan0.0f
diwakili oleh pola bit yang berbeda (bit tanda berbeda), tetapi bandingkan sebagai sama dengan==
(tetapi tidak denganequals
). Asumsi Anda bahwa==
perbandingan bitwise, secara umum, salah.Berikut ini adalah diskusi yang sangat panjang (tapi semoga bermanfaat) tentang hal ini dan banyak masalah floating point lainnya yang mungkin Anda temui: Apa yang Harus Diketahui Setiap Ilmuwan Komputer Tentang Aritmatika Titik Apung
sumber
Anda dapat menggunakan Float.floatToIntBits ().
sumber
Pertama-tama, apakah mereka mengambang atau mengambang? Jika salah satunya adalah Float, Anda harus menggunakan metode equals (). Juga, mungkin yang terbaik untuk menggunakan metode statis Float.compare.
sumber
Berikut ini secara otomatis menggunakan presisi terbaik:
Tentu saja, Anda dapat memilih lebih dari 5 ULP ('unit di tempat terakhir').
Jika Anda masuk ke pustaka Apache Commons,
Precision
kelas memilikicompareTo()
danequals()
dengan epsilon dan ULP.sumber
double
menutupi ini.Anda mungkin ingin menjadi ==, tetapi 123.4444444444443! = 123.444444444444242
sumber
Jika Anda * harus * menggunakan float, strictfp kata kunci mungkin berguna.
http://en.wikipedia.org/wiki/strictfp
sumber
Dua perhitungan berbeda yang menghasilkan bilangan real yang sama tidak harus menghasilkan bilangan floating point yang sama. Orang yang menggunakan == untuk membandingkan hasil perhitungan biasanya pada akhirnya terkejut oleh hal ini, sehingga peringatan membantu menandai apa yang mungkin menjadi bug yang halus dan sulit untuk diperbanyak.
sumber
Apakah Anda berurusan dengan kode outsourcing yang akan menggunakan float untuk hal-hal yang bernama sectionID dan currentSectionID? Hanya penasaran.
@ Bill K: "Representasi biner float agak mengganggu." Bagaimana? Bagaimana Anda melakukannya dengan lebih baik? Ada angka-angka tertentu yang tidak dapat direpresentasikan dalam basis apa pun dengan benar, karena tidak pernah berakhir. Pi adalah contoh yang bagus. Anda hanya bisa memperkirakannya. Jika Anda memiliki solusi yang lebih baik, hubungi Intel.
sumber
Seperti disebutkan dalam jawaban lain, ganda dapat memiliki penyimpangan kecil. Dan Anda dapat menulis metode Anda sendiri untuk membandingkannya menggunakan deviasi yang "dapat diterima". Namun ...
Ada kelas apache untuk membandingkan ganda: org.apache.commons.math3.util.Precision
Ini berisi beberapa konstanta yang menarik:
SAFE_MIN
danEPSILON
, yang merupakan penyimpangan maksimum yang mungkin dari operasi aritmatika sederhana.Ini juga menyediakan metode yang diperlukan untuk membandingkan, menyamakan atau membulatkan ganda. (menggunakan ulp atau deviasi absolut)
sumber
Dalam satu jawaban yang bisa saya katakan, Anda harus menggunakan:
Untuk membuat Anda belajar lebih banyak tentang penggunaan operator terkait dengan benar, saya menguraikan beberapa kasus di sini: Secara umum, ada tiga cara untuk menguji string di Jawa. Anda bisa menggunakan ==, .equals (), atau Objects.equals ().
Bagaimana mereka berbeda? == tes untuk kualitas referensi dalam string yang berarti mencari tahu apakah kedua benda itu sama. Di sisi lain, .equals () menguji apakah kedua string memiliki nilai yang sama secara logis. Akhirnya, Objects.equals () menguji setiap null di dua string kemudian menentukan apakah akan memanggil .equals ().
Operator ideal untuk digunakan
Nah ini telah menjadi subyek banyak perdebatan karena masing-masing dari tiga operator memiliki kekuatan dan kelemahan yang unik. Contoh, == sering merupakan opsi yang disukai ketika membandingkan referensi objek, tetapi ada beberapa kasus di mana ia tampaknya membandingkan nilai string juga.
Namun, apa yang Anda dapatkan adalah nilai jatuh karena Java menciptakan ilusi bahwa Anda membandingkan nilai tetapi dalam arti sebenarnya Anda tidak. Pertimbangkan dua kasus di bawah ini:
Kasus 1:
Kasus 2:
Jadi, lebih baik menggunakan setiap operator saat menguji atribut khusus yang dirancang untuknya. Tetapi dalam hampir semua kasus, Objects.equals () adalah operator yang lebih universal sehingga pengalaman pengembang web memilih untuk itu.
Di sini Anda bisa mendapatkan detail lebih lanjut: http://fluentthemes.com/use-compare-strings-java/
sumber
Cara yang benar adalah
sumber
Float.compare(1.1 + 2.2, 3.3) != 0
Salah satu cara untuk mengurangi kesalahan pembulatan adalah menggunakan double daripada float. Ini tidak akan membuat masalah hilang, tetapi mengurangi jumlah kesalahan dalam program Anda dan float hampir tidak pernah menjadi pilihan terbaik. MENURUT OPINI SAYA.
sumber