Saya ingin membandingkan dua float di PHP, seperti dalam kode contoh ini:
$a = 0.17;
$b = 1 - 0.83; //0.17
if($a == $b ){
echo 'a and b are same';
}
else {
echo 'a and b are not same';
}
Dalam kode ini ia mengembalikan hasil else
kondisi bukannya if
kondisi, meskipun $a
dan $b
sama. Apakah ada cara khusus untuk menangani / membandingkan float di PHP?
Jika ya maka tolong bantu saya untuk menyelesaikan masalah ini.
Atau apakah ada masalah dengan konfigurasi server saya?
php
floating-point
Santosh Sonarikar
sumber
sumber
a and b are same
. Apakah ini kode lengkap Anda?floating-point
deskripsi tag? stackoverflow.com/tags/floating-point/info Itulah perilaku yang mungkin Anda temui dalam bahasa pemrograman apa pun, saat menggunakan angka floating-point. Lihat misalnya stackoverflow.com/questions/588004/is-javascripts-math-brokenJawaban:
Jika Anda melakukannya seperti ini mereka harus sama. Tetapi perhatikan bahwa karakteristik nilai floating-point adalah bahwa perhitungan yang tampaknya menghasilkan nilai yang sama tidak perlu benar-benar identik. Jadi jika
$a
itu literal.17
dan$b
tiba di sana melalui perhitungan, bisa jadi keduanya berbeda, meskipun keduanya menampilkan nilai yang sama.Biasanya Anda tidak pernah membandingkan nilai floating-point untuk kesetaraan seperti ini, Anda perlu menggunakan perbedaan terkecil yang dapat diterima:
Sesuatu seperti itu.
sumber
abs($a-$b)
>abs(($a-$b)/$b)
0.10000000000000000555111512312578270211815834045410156
biasanya tidak ada gunanya dan mereka lebih suka0.1
sebaliknya. Dan menulis nomor sehingga dapat dibaca lagi dengan cara yang persis sama. Seperti yang Anda lihat, ini tidak sejelas yang Anda bayangkan. Dan sebagai catatan, Anda masih ingin membandingkan angka floating-point seperti yang saya tunjukkan karena Anda dapat mencapai$a
dan$b
melalui perhitungan berbeda yang dapat membuatnya berbeda.a=b=0
dan jikaa
adalah yang terkecil yang mungkin tidak ada nilai positif nol danb
merupakan nilai terkecil yang mungkin bukan nol negatif, tes tersebut akan gagal. Beberapa informasi bagus di sini: floating-point-gui.de/errors/comparison$b
? yang pengguna PHP hanya melakukanif(abs($a-$b) < $epsilon)
yang manual MySQL juga melakukan hal yang samaHAVING ABS(a - b) <= 0.0001
$a == $b == 0
, tetapi sudah jauh lebih umum daripada kesalahan absolut. Jika$a
dan$b
ada dalam jutaan, maka AndaEPSILON
harus sangat berbeda daripada jika$a
dan$b
berada di suatu tempat dekat0
. Lihat tautan Dom di atas untuk diskusi yang lebih baik tentang iniBaca peringatan merah di manual terlebih dahulu. Anda tidak boleh membandingkan pelampung untuk kesetaraan. Anda harus menggunakan teknik epsilon.
Sebagai contoh:
di mana
PHP_FLOAT_EPSILON
konstanta mewakili angka yang sangat kecil (Anda harus mendefinisikannya dalam versi PHP lama sebelum 7.2)sumber
EPSILON
sini adalah konstanta yang ditentukan pengguna secara sewenang-wenang. PHP tidak memiliki konstanta bawaan yang mewakili ide spesifik arsitektur epsilon. (Lihat jugaget_defined_constants
.)PHP_FLOAT_EPSILON
Angka positif terkecil yang dapat diwakili x, sehingga x + 1.0! = 1.0. Tersedia pada PHP 7.2.0.echo $a - $b; /* 5.6843418860808E-14 */ echo PHP_FLOAT_EPSILON; /* 2.2204460492503E-16 */
Pertanyaannya adalah bagaimana Anda ingin mendefinisikan "sama" untuk aplikasi Anda, seberapa dekat angkanya harus dianggap sama.Atau coba gunakan fungsi bc matematika:
Hasil:
sumber
bccomp()
mengambil string sebagai argumen. Pokoknya Anda bisa menggunakanPHP_FLOAT_DIG
untuk argumen skala.Seperti yang dikatakan sebelumnya, berhati-hatilah saat melakukan perbandingan floating point (apakah sama dengan, lebih besar, atau kurang dari) di PHP. Namun, jika Anda hanya tertarik pada beberapa digit signifikan, Anda dapat melakukan sesuatu seperti:
Penggunaan pembulatan ke 2 tempat desimal (atau 3, atau 4) akan menyebabkan hasil yang diharapkan.
sumber
loose_float_compare
sehingga jelas apa yang sedang terjadi.bccomp($a, $b, 2)
lebih unggul daripada solusi Anda. Dalam contoh ini, 2 adalah ketepatan. Anda dapat mengaturnya ke jumlah floating point yang ingin Anda bandingkan.Akan lebih baik menggunakan perbandingan PHP asli :
sumber
Jika Anda memiliki nilai floating point untuk dibandingkan dengan kesetaraan, cara sederhana untuk menghindari risiko strategi pembulatan internal OS, bahasa, prosesor atau sebagainya, adalah membandingkan representasi string dari nilai-nilai tersebut.
Anda dapat menggunakan salah satu dari yang berikut ini untuk menghasilkan hasil yang diinginkan: https://3v4l.org/rUrEq
Pengecoran Tipe String
Penggabungan string
fungsi strval
Representasi string jauh lebih rewel daripada mengapung ketika datang untuk memeriksa kesetaraan.
sumber
(string)
operasi pemeran dilakukan dengan referensi, mengubah deklarasi asli? Jika demikian, ini bukan kasus 3v4l.org/CraasJika Anda memiliki jumlah kecil angka desimal yang terbatas yang dapat diterima, berikut ini berfungsi dengan baik (meskipun dengan kinerja lebih lambat daripada solusi epsilon):
sumber
Ini berfungsi untuk saya di PHP 5.3.27.
sumber
Untuk PHP 7.2, Anda dapat bekerja dengan PHP_FLOAT_EPSILON ( http://php.net/manual/en/reserved.constants.php ):
sumber
==
dan!=
tapi tidak>
,>=
,<
,<=
Jika Anda menulisnya seperti itu mungkin akan berhasil, jadi saya bayangkan Anda menyederhanakannya untuk pertanyaan. (Dan menjaga pertanyaan tetap sederhana dan ringkas biasanya adalah hal yang sangat baik.)
Tetapi dalam kasus ini saya membayangkan satu hasil adalah perhitungan dan satu hasil adalah konstan.
Ini melanggar aturan utama pemrograman titik mengambang: Jangan pernah melakukan perbandingan kesetaraan.
Alasan untuk ini agak 1 halus tetapi yang penting untuk diingat adalah bahwa mereka biasanya tidak berfungsi (kecuali, ironisnya, untuk nilai integral) dan bahwa alternatifnya adalah perbandingan fuzzy di sepanjang baris:
1. Salah satu masalah utama melibatkan cara kita menulis angka dalam program. Kami menulisnya sebagai string desimal, dan akibatnya sebagian besar fraksi yang kami tulis tidak memiliki representasi mesin yang tepat. Mereka tidak memiliki bentuk hingga yang pasti karena mereka ulangi dalam bentuk biner. Setiap fraksi mesin adalah bilangan rasional dari bentuk x / 2 n . Sekarang, konstanta adalah desimal dan setiap konstanta desimal adalah bilangan rasional dari bentuk x / (2 n * 5 m ). Angka 5 m itu ganjil, jadi tidak ada faktor 2 n untuk mereka. Hanya ketika m == 0 adakah representasi terbatas dalam ekspansi biner dan desimal dari fraksi. Jadi, 1,25 tepat karena 5 / (2 2 * 5 0) tetapi 0,1 bukan karena itu 1 / (2 0 * 5 1 ). Faktanya, dalam seri 1.01 .. 1.99 hanya 3 angka yang benar-benar mewakili: 1.25, 1.50, dan 1.75.
sumber
round($float, 3) == round($other, 3)
Berikut adalah solusi untuk membandingkan titik mengambang atau angka desimal
Keluarkan
decimal
variabel kestring
dan Anda akan baik-baik saja.sumber
Membandingkan float untuk kesetaraan memiliki algoritma O (n) naif.
Anda harus mengonversi setiap nilai float ke string, lalu membandingkan setiap digit mulai dari sisi kiri setiap representasi string float menggunakan operator perbandingan integer. PHP akan melakukan autocast digit pada setiap posisi indeks ke integer sebelum perbandingan. Digit pertama yang lebih besar dari yang lain akan memutus loop dan menyatakan float bahwa itu milik sebagai yang lebih besar dari keduanya. Rata-rata, akan ada perbandingan 1/2 * n. Untuk mengapung yang sama satu sama lain, akan ada n perbandingan. Ini adalah skenario terburuk untuk algoritma. Skenario kasus terbaik adalah bahwa digit pertama dari setiap float berbeda, hanya menyebabkan satu perbandingan.
Anda tidak dapat menggunakan OPERATOR PERBANDINGAN INTEGER pada nilai float mentah dengan tujuan menghasilkan hasil yang bermanfaat. Hasil dari operasi tersebut tidak memiliki arti karena Anda tidak membandingkan bilangan bulat. Anda melanggar domain masing-masing operator yang menghasilkan hasil yang tidak berarti. Ini berlaku untuk perbandingan delta juga.
Gunakan operator perbandingan integer untuk apa yang mereka dirancang untuk: membandingkan integer.
SOLUSI YANG DISEDIAKAN:
sumber
2019
TL; DR
Gunakan fungsi saya di bawah, seperti ini
if(cmpFloats($a, '==', $b)) { ... }
cmpFloats($a, '<=', $b)
vs.bccomp($a, $b) <= -1
Ringkasan
Saya akan mengungkap misterinya.
Jadi jika Anda mencoba yang di bawah ini, itu akan sama:
Bagaimana cara mendapatkan nilai float yang sebenarnya?
Bagaimana Anda bisa membandingkan?
==
dan!=
, Anda bisa mengetikkannya ke string, itu akan bekerja dengan sempurna:Ketik cast dengan string :
Atau typecast dengan
number_format()
:Peringatan:
Hindari solusi yang melibatkan memanipulasi float secara matematis (mengalikan, membagi, dll) kemudian membandingkan, sebagian besar mereka akan memecahkan beberapa masalah dan memperkenalkan masalah lain.
Solusi yang Disarankan
Saya telah membuat fungsi PHP murni (tidak perlu depenedies / pustaka / ekstensi). Memeriksa dan membandingkan setiap digit sebagai string. Juga berfungsi dengan angka negatif.
sumber
Fungsi dari @evilReiko memiliki beberapa bug seperti ini:
Dalam fungsi saya, saya telah memperbaiki bug ini, tetapi bagaimanapun juga dalam beberapa kasus fungsi ini mengembalikan jawaban yang salah:
Fungsi tetap untuk membandingkan mengapung
Jawab untuk pertanyaan Anda
sumber
Ini adalah kelas yang berguna dari perpustakaan pribadi saya untuk berurusan dengan angka floating point. Anda dapat menyesuaikannya sesuai keinginan Anda dan memasukkan solusi apa pun yang Anda suka ke metode kelas :-).
sumber
Jawaban sederhana:
sumber