Penjagaan ekstrem diperlukan apabila mengendalikan nilai titik tetap, terutamanya apabila membangun dengan PHP dan MySQL. Artikel ini akan meneroka halangan dan butiran yang dihadapi ketika menggunakan sambungan PHP BCMATH, pemprosesan ekspresi titik tetap MySQL, dan data titik tetap yang berterusan dari PHP ke MySQL. Walaupun beberapa cabaran, kami akan cuba mencari cara mengendalikan nilai titik tetap dan mengelakkan kerugian ketepatan.
ringkasan mata utama
Untuk operasi matematik ketepatan sewenang -wenang, PHP menyediakan kalkulator binari yang menyokong sebarang saiz dan nombor ketepatan yang dinyatakan sebagai rentetan.Oleh itu, parameter fungsi BCMATH harus diwakili sebagai rentetan. Melewati pembolehubah angka ke fungsi BCMATH boleh mengakibatkan hasil yang salah, kehilangan ketepatan yang sama seperti yang berlaku ketika merawat nilai ganda sebagai rentetan.
Kes 1
echo bcmul(776.210000, '100', 10) . PHP_EOL; echo bcmul(776.211000, '100', 10) . PHP_EOL; echo bcmul(776.210100, '100', 10) . PHP_EOL; echo bcmul(50018850776.210000, '100', 10) . PHP_EOL; echo bcmul(50018850776.211000, '100', 10) . PHP_EOL; echo bcmul(50018850776.210100, '100', 10) . PHP_EOL;
Jangan lulus pembolehubah angka ke fungsi bcmath, hanya nilai rentetan yang mewakili nombor diluluskan. Walaupun nombor titik terapung tidak diproses, bcmath akan mengeluarkan hasil yang pelik:
<code>77621.00 77621.100 77621.0100 5001885077621.00 5001885077621.100 5001885077621.00 //此处可见精度损失</code>
echo bcmul('10', 0.0001, 10) . PHP_EOL; echo bcmul('10', 0.00001, 10) . PHP_EOL; echo 10*0.00001 . PHP_EOL;
Alasannya ialah BCMATH menukarkan parameternya kepada rentetan, dan dalam beberapa kes, perwakilan rentetan nombor mempunyai perwakilan eksponen.
<code>0.0010 0 // 这真的很奇怪!!! 0.0001</code>
echo bcmul('10', '1e-4', 10) . PHP_EOL; // 也输出 0
kes 2
dankes 3 : Walau bagaimanapun, menggunakan transformasi yang sama akan memusnahkan tingkah laku "betul"
kes 1$val = sprintf("%.10f", '1e-5'); echo bcmul('10', $val, 10) . PHP_EOL; // 给我们 0.0001000000
Jadi penyelesaian Sprintf tidak berfungsi dengan BCMATH. Dengan mengandaikan semua input pengguna adalah rentetan, kami boleh melaksanakan pengesahan mudah yang menangkap bilangan semua notasi eksponen dan menukarkannya dengan betul. Teknik ini dilaksanakan dalam PHP-Bignumbers, jadi kami dapat dengan selamat lulus dalam parameter seperti 1E-20 dan 50018850776.2101 tanpa kehilangan ketepatan.
Jangan gunakan nombor titik terapung sebagai parameter operasi titik tetap dalam fungsi lanjutan BCMath PHP. Gunakan hanya rentetan.
Apabila menggunakan operasi sambungan BCMATH, perhatikan parameter notasi eksponen. Fungsi BCMATH tidak mengendalikan parameter eksponen dengan betul (seperti "1E-8"), jadi mereka harus ditukar secara manual. Berhati -hatilah untuk tidak menggunakan sprintf atau teknik penukaran yang serupa kerana ini akan mengakibatkan kehilangan ketepatan.
Perpustakaan PHP-Bignumbers boleh digunakan, yang boleh mengendalikan parameter input dalam bentuk eksponen dan menyediakan pengguna dengan operasi matematik tetap. Walau bagaimanapun, prestasinya tidak begitu baik dengan sambungan BCMATH, jadi ia adalah tradeoff antara pakej dan prestasi yang mantap.
Di MySQL, nombor titik tetap diproses menggunakan jenis lajur perpuluhan. Anda boleh membaca dokumentasi MySQL rasmi untuk jenis data dan operasi matematik yang tepat.
Bahagian yang paling menarik ialah bagaimana MySQL mengendalikan ungkapan:
pemprosesan ekspresi angka bergantung kepada jenis nilai yang terkandung dalam ungkapan:
Jika ada penghampiran, ungkapan itu adalah penghampiran dan dikira menggunakan operasi titik terapung.
Jika tiada penghampiran wujud, ungkapan hanya mengandungi nilai yang tepat. Jika sebarang nilai tepat mengandungi bahagian pecahan (nilai selepas titik perpuluhan), ungkapan dikira menggunakan aritmetik tepat perpuluhan dengan ketepatan 65 digit. Perkataan "ketepatan" adalah terhad oleh apa yang boleh diwakili dalam binari. Sebagai contoh, 1.0/3.0 boleh menjadi anggaran kepada .333 ... menggunakan notasi perpuluhan, tetapi tidak boleh ditulis sebagai nombor yang tepat, SO (1.0/3.0)*3.0 dikira secara tidak tepat sebagai 1.0.
Jika tidak, ungkapan itu hanya mengandungi nilai integer. Ekspresi adalah tepat dan dikira menggunakan aritmetik integer dengan ketepatan yang sama seperti BIGINT (64 bit).
Jika ungkapan angka mengandungi sebarang rentetan, ia ditukar kepada nilai terapung dua ketepatan, dan ungkapannya adalah nilai anggaran.
Ini adalah contoh ringkas yang menunjukkan kes bahagian perpuluhan:
echo bcmul(776.210000, '100', 10) . PHP_EOL; echo bcmul(776.211000, '100', 10) . PHP_EOL; echo bcmul(776.210100, '100', 10) . PHP_EOL; echo bcmul(50018850776.210000, '100', 10) . PHP_EOL; echo bcmul(50018850776.211000, '100', 10) . PHP_EOL; echo bcmul(50018850776.210100, '100', 10) . PHP_EOL;
Ini kelihatan mudah, tetapi mari kita lihat bagaimana untuk mengendalikannya dalam PHP.
Jadi sekarang kita perlu meneruskan nilai titik tetap dari PHP ke MySQL. Cara yang betul ialah menggunakan penyataan pra -proses dan ruang letak dalam pertanyaan. Kemudian kami mengikat parameter, semuanya selamat dan boleh dipercayai.
<code>77621.00 77621.100 77621.0100 5001885077621.00 5001885077621.100 5001885077621.00 //此处可见精度损失</code>
Apabila kita mengikat nilai kepada pemegang tempat pernyataan, kita boleh menentukan jenisnya melalui parameter ketiga bindValue. Jenis yang mungkin diwakili oleh pemalar pdo :: param_bool, pdo :: param_null, pdo :: param_int, pdo :: param_str, pdo :: param_lob, dan pdo :: param_stmt. Oleh itu, masalahnya ialah lanjutan PHP PDO tidak mempunyai jenis parameter perpuluhan untuk mengikat. Akibatnya, semua ungkapan matematik dalam pertanyaan dianggap sebagai ungkapan titik terapung, bukannya ungkapan titik tetap.
Jika kita mahu memanfaatkan pernyataan pra -proses dan menggunakan nombor titik tetap, cara terbaik adalah untuk melaksanakan semua matematik dalam PHP dan menyimpan hasilnya kepada MySQL.
echo bcmul('10', 0.0001, 10) . PHP_EOL; echo bcmul('10', 0.00001, 10) . PHP_EOL; echo 10*0.00001 . PHP_EOL;
kami sampai pada kesimpulan berikut:
Kaedah kegemaran peribadi saya adalah yang pertama: Lakukan semua matematik dalam PHP. Saya bersetuju bahawa PHP dan MySQL mungkin bukan pilihan terbaik untuk aplikasi yang memerlukan operasi matematik yang tepat, tetapi jika anda memilih timbunan teknologi ini, adalah baik untuk mengetahui cara mengendalikannya dengan betul.
(bahagian Soalan Lazim ditinggalkan kerana batasan ruang. Jika perlu, bahagian FAQ boleh dihasilkan secara berasingan.)
Atas ialah kandungan terperinci Matematik Titik Tetap di PHP dengan BCMATH, Kes Kehilangan Ketepatan. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!