Rumah > pembangunan bahagian belakang > tutorial php > Matematik Titik Tetap di PHP dengan BCMATH, Kes Kehilangan Ketepatan

Matematik Titik Tetap di PHP dengan BCMATH, Kes Kehilangan Ketepatan

Joseph Gordon-Levitt
Lepaskan: 2025-02-20 09:17:10
asal
489 orang telah melayarinya

Fixed Point Math in PHP with BCMath, precision loss cases

cabaran dan petua untuk operasi berangka titik tetap dalam php dan mysql

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

    Pelanjutan BCMATH dalam PHP menyokong operasi matematik ketepatan sewenang -wenang, tetapi boleh mengakibatkan kerugian ketepatan jika pembolehubah berangka disampaikan kepada fungsinya. Nilai rentetan yang mewakili nombor harus digunakan untuk mengelakkan masalah ini.
  • MySQL menyokong ekspresi angka titik tetap, bagaimanapun, jika mana -mana pengendali dalam format eksponen atau rentetan, ia dianggap sebagai nombor titik terapung. Sambungan PHP PDO tidak mempunyai jenis parameter perpuluhan untuk mengikat, yang mungkin mengakibatkan hasil yang tidak tepat.
  • Untuk melaksanakan operasi matematik yang tepat dalam aplikasi PHP MySQL, semua operasi boleh diproses dalam PHP dan data hanya dapat diteruskan ke MySQL menggunakan pernyataan Insert atau Update. Sebagai alternatif, anda boleh membina pertanyaan SQL secara manual untuk memastikan bahawa semua ungkapan matematik SQL diwakili dalam nombor perpuluhan.
Masalah BCMATH

dokumentasi bcmath menyatakan:

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

Hasilnya ialah:

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;
Salin selepas log masuk
Salin selepas log masuk

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>
Salin selepas log masuk
Salin selepas log masuk
Case 2

Hasilnya ialah:

echo bcmul('10', 0.0001, 10) . PHP_EOL;
echo bcmul('10', 0.00001, 10) . PHP_EOL;
echo 10*0.00001 . PHP_EOL;
Salin selepas log masuk
Salin selepas log masuk

Alasannya ialah BCMATH menukarkan parameternya kepada rentetan, dan dalam beberapa kes, perwakilan rentetan nombor mempunyai perwakilan eksponen.

<code>0.0010
0 // 这真的很奇怪!!!
0.0001</code>
Salin selepas log masuk
Case 3

PHP adalah bahasa lemah jenis yang, dalam beberapa kes, tidak mempunyai kawalan ketat ke atas input-berfikir untuk mengendalikan sebanyak mungkin permintaan.

echo bcmul('10', '1e-4', 10) . PHP_EOL; // 也输出 0
Salin selepas log masuk
Sebagai contoh, kita boleh "memperbaiki"

kes 2

dan

kes 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
Salin selepas log masuk
:

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.

BCMATH Garis Panduan Akhir

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.

mysql dan nombor titik tetap

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;
Salin selepas log masuk
Salin selepas log masuk

Ini kelihatan mudah, tetapi mari kita lihat bagaimana untuk mengendalikannya dalam PHP.

operasi matematik yang tepat dalam php dan mysql

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>
Salin selepas log masuk
Salin selepas log masuk

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;
Salin selepas log masuk
Salin selepas log masuk

Kesimpulan

kami sampai pada kesimpulan berikut:

  • Jangan gunakan nombor titik terapung sebagai parameter operasi titik tetap dalam fungsi lanjutan BCMath PHP. Gunakan hanya rentetan.
  • BCMATH Extension tidak terpakai kepada nombor rentetan untuk notasi eksponen.
  • MySQL menyokong ekspresi angka titik tetap, tetapi semua operan mestilah dalam format perpuluhan. Jika sekurang -kurangnya satu parameter berada dalam format eksponen atau rentetan, ia dianggap sebagai nombor titik terapung dan ekspresi dikira sebagai nombor titik terapung.
  • PHP PDO Extension tidak mempunyai jenis parameter perpuluhan, jadi jika anda menggunakan pernyataan pra -proses dan mengikat parameter dalam ekspresi SQL yang mengandungi operan titik tetap, anda tidak akan mendapat hasil yang tepat.
  • Untuk melakukan operasi matematik yang tepat dalam aplikasi PHP MySQL, anda boleh memilih dari dua kaedah. Kaedah pertama adalah untuk memproses semua operasi dalam PHP dan hanya gunakan penyataan memasukkan atau kemas kini untuk meneruskan data ke MySQL. Dalam kes ini, anda boleh menggunakan penyataan preprocessed dan pengikat parameter. Pendekatan kedua adalah untuk membina pertanyaan SQL secara manual (anda masih boleh menggunakan pernyataan pra -proses, tetapi anda perlu melarikan diri dari parameter itu sendiri) supaya semua ungkapan matematik SQL dinyatakan dalam nombor perpuluhan.

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!

Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Artikel terbaru oleh pengarang
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan