Bagaimana cara menangani nilai moneter dalam PHP dan MySql?


16

Saya telah mewarisi tumpukan besar kode warisan yang ditulis dalam PHP di atas basis data MySQL. Hal yang saya perhatikan adalah aplikasi yang digunakan doublesuntuk penyimpanan dan manipulasi data.

Sekarang saya menemukan banyak posting yang menyebutkan bagaimana doubletidak cocok untuk operasi moneter karena kesalahan pembulatan. Namun, saya belum menemukan solusi lengkap tentang bagaimana nilai moneter harus ditangani dalam kode PHP dan disimpan dalam database MySQL.

Apakah ada praktik terbaik dalam menangani uang secara khusus dalam PHP?

Hal yang saya cari adalah:

  1. Bagaimana seharusnya data disimpan dalam database? jenis kolom? ukuran?
  2. Bagaimana seharusnya penanganan data dalam penambahan normal, pengurangan. perkalian atau pembagian?
  3. Kapan saya harus membulatkan nilainya? Berapa banyak pembulatan dapat diterima jika ada?
  4. Apakah ada perbedaan antara penanganan nilai moneter besar dan rendah?

Catatan: Sebuah SANGAT disederhanakan kode contoh dari bagaimana saya bisa menemukan nilai-nilai uang dalam kehidupan sehari-hari (berbagai masalah keamanan diabaikan untuk penyederhanaan Tentu saja dalam kehidupan nyata saya tidak akan menggunakan kode saya seperti ini.):

$a= $_POST['price_in_dollars']; //-->(ex: 25.06) will be read as a string should it be cast to double?
$b= $_POST['discount_rate'];//-->(ex: 0.35) value will always be less than 1
$valueToBeStored= $a * $b; //--> any hint here is welcomed 

$valueFromDatabase= $row['price']; //--> price column in database could be double, decimal,...etc.

$priceToPrint=$valueFromDatabase * 0.25; //again cast needed or not?

Saya harap Anda menggunakan kode sampel ini sebagai sarana untuk membawa lebih banyak kasus penggunaan dan tidak menerimanya secara harfiah.

Pertanyaan Bonus Jika saya menggunakan ORM seperti Doctrine atau PROPEL, betapa berbedanya menggunakan uang dalam kode saya.


1
Saya tidak tahu PHP tetapi mengetahui terminologi sangat berharga dalam skenario ini untuk google-fu, istilah yang Anda cari adalah "arbitrary precision" di mana google merespons dengan php.net/manual/en/book.bc.php
Jimmy Hoffa

1
@ Jimmy Hoffa: presisi arbiter umumnya bukan yang Anda butuhkan, sesuatu yang dapat secara akurat mewakili dan bekerja dengan pecahan desimal. Tentu saja, Anda sering menemukan keduanya digabungkan, tetapi misalnya decimaltipe dalam C # memiliki presisi terbatas tetapi sangat cocok untuk nilai moneter.
Michael Borgwardt

1
@MichaelBorgwardt benar, saya lupa tentang jenis rasional karena begitu banyak bahasa tidak memilikinya. Panggilan yang bagus.
Jimmy Hoffa

Setelah banyak bekerja dengan mata uang dan kode warisan, saya katakan pergi untuk menyimpannya dalam bilangan bulat dan menggunakan sen, bukan dolar. Namun waspadalah, 32 bilangan bulat dapat menampung jumlah yang sangat kecil.4) bilangan bulat 32 bit hanya dapat menampung jumlah 21 juta jika tidak ditandatangani dan menggunakan sen.
Pieter B

Selain itu, kode sampel Anda yang menunjukkan harga dan diskon yang dikirim POST menakutkan saya. Mudah-mudahan ini sangat disederhanakan sehingga tidak mencerminkan penggunaan nyata, tetapi pada nilai nominal, sepertinya Anda mempercayai browser untuk memberi tahu Anda berapa harga suatu item dengan POSTing kembali bidang yang tersembunyi atau semacamnya.
Carson63000

Jawaban:


6

Ini bisa sangat sulit untuk menangani angka dengan PHP / MySQL. Jika Anda menggunakan desimal (10,2) dan nomor Anda lebih panjang atau memiliki ketepatan yang lebih tinggi maka akan terpotong tanpa kesalahan (kecuali Anda mengatur mode yang tepat untuk server db Anda).

Untuk menangani nilai besar atau nilai presisi tinggi, Anda dapat menggunakan perpustakaan seperti BCMath sehingga Anda dapat melakukan operasi dasar pada angka besar dan menjaga presisi yang diperlukan.

Saya tidak yakin perhitungan persis apa yang akan Anda buat, tetapi Anda juga harus ingat bahwa (0,22 * 0,4576) + (0,78 * 0,4576) tidak akan sama dengan 0,4576 jika Anda tidak akan menggunakan presisi yang tepat melalui proses.

Ukuran maksimum DECIMAL di MySQL adalah 65 sehingga harus lebih dari cukup untuk tujuan apa pun. Jika Anda menggunakan jenis bidang DECIMAL itu akan dikembalikan sebagai string terlepas dari penggunaan ORM atau hanya PDO / mysql (i).

Bagaimana seharusnya data disimpan dalam database? jenis kolom? ukuran?

DECIMAL dengan presisi yang Anda butuhkan. Jika Anda menggunakan nilai tukar maka Anda akan membutuhkan setidaknya empat tempat desimal

Bagaimana seharusnya penanganan data dalam penambahan normal, pengurangan. perkalian atau pembagian?

Gunakan BCMath untuk berada di sisi yang aman dan mengapa menggunakan float mungkin bukan ide yang baik

Kapan saya harus membulatkan nilainya? Berapa banyak pembulatan dapat diterima jika ada?

Untuk nilai moneter normal dua tempat desimal dapat diterima tetapi Anda mungkin perlu lebih banyak jika misalnya Anda menggunakan nilai tukar.

Apakah ada perbedaan antara penanganan nilai moneter besar dan rendah?

Tergantung pada apa yang Anda maksud dengan besar. Jelas ada perbedaan antara penanganan angka dengan presisi tinggi.


9

Pekerjaan sederhana di sekitar adalah menyimpannya sebagai bilangan bulat. 99,99 disimpan sebagai 9999. Jika ini tidak berhasil (dan ada banyak alasan mengapa ini bisa menjadi pilihan yang buruk), Anda dapat menggunakan jenis Desimal. http://dev.mysql.com/doc/refman/5.0/en/precision-math-decimal-changes.html di sisi mysql. Di sisi php saya menemukan ini /programming/3244094/decimal-type-in-php yang mungkin apa yang Anda cari.

Pertanyaan bonus: Sulit dikatakan. Orm akan bekerja berdasarkan tipe data yang dipilih. Saya akan mengatakan bahwa Anda dapat melakukan beberapa hal dengan abstraksi untuk membantu tetapi masalah khusus ini tidak diatasi hanya dengan pindah ke ORM.


1
FWIW, Drupal Commerce menggunakan trik 9999 untuk menyimpan harganya.
Florian Margaine

1
"dan ada banyak alasan mengapa ini bisa menjadi pilihan yang buruk" Apakah Anda akan berbagi beberapa masalah dengan praktik ini?
Songo


@MichaelBorgwardt Terima kasih atas informasinya, Songo minta maaf atas keterlambatannya, angin topan membawa kami keluar :)
Ominus

3

Saya akan mencoba memasukkan pengalaman saya dalam hal ini:

Hal yang saya cari adalah:

Bagaimana seharusnya data disimpan dalam database? jenis kolom? ukuran?

Saya telah menggunakan DECIMAL(10,2)untuk mysql tanpa masalah (8 selesai dan 2 desimal == 99,999,999,99 == jumlah besar), tetapi ini tergantung pada kisaran uang yang harus Anda tutupi. Jumlah besar harus diambil dengan hati-hati (misalnya nilai float OS max). Pada bagian desimal saya menggunakan 2 nilai untuk menghindari nilai truncate atau pembulatan. Mengambil uang ada beberapa kasus di mana Anda membutuhkan lebih banyak desimal (dalam hal ini Anda perlu memastikan pengguna akan bekerja dengan semuanya, jika tidak, ini adalah data yang tidak berguna)

Bagaimana seharusnya penanganan data dalam penambahan normal, pengurangan. perkalian atau pembagian?

Bekerja dengan satu mata uang dan tabel pertukaran (dengan tanggal). Dengan cara ini Anda memastikan bahwa Anda akan selalu menyimpan jumlah yang benar. Ekstra: menyimpan nilai lengkap dan membuat tampilan dengan hasil perhitungan. Ini akan membantu Anda memperbaiki nilai dengan cepat

Kapan saya harus membulatkan nilainya? Berapa banyak pembulatan dapat diterima jika ada?

Sekali lagi, tergantung pada kisaran uang sistem Anda. Selalu berpikir dalam istilah KISS kecuali Anda harus jatuh dalam kekacauan pertukaran mata uang

Apakah ada perbedaan antara penanganan nilai moneter besar dan rendah?

Bergantung pada OS dan bahasa pemrograman Anda, Anda selalu perlu memeriksa nilai maks dan min


Terima kasih atas jawabannya, tetapi jika saya harus menangani sesuatu seperti $valueToBeStored= $a * $b;jika $adan $bkeduanya dibaca sebagai desimal dari database saya pikir mereka akan dilemparkan ke doubledalam PHP, kan? apakah itu akan mempengaruhi angka?
Songo

$adan $bdiambil dari db? jadi, dalam contoh saya Anda tidak perlu menyimpan $valueToBeStoredkarena Anda akan selalu memiliki sumber $adan $bdata. Jadi, Anda dapat bekerja secara terprogram nilai dalam suatu fungsi atau lebih atau membuat tampilan mysql dengan hasil kolom. Dengan cara ini, jika ada nilai yang harus diubah, Anda tidak perlu khawatir mengubah beberapa tempat (rawan kesalahan)
Alwin Kesler
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.