Saya dapat menemukan banyak pertanyaan tentang perpustakaan untuk digunakan untuk mewakili jumlah dalam beberapa mata uang. Dan tentang masalah tua mengapa Anda tidak harus menyimpan mata uang sebagai nomor poin mengambang IEEE 754. Tetapi sepertinya saya tidak dapat menemukan apa pun lagi. Tentunya ada banyak lagi yang perlu diketahui tentang mata uang dalam penggunaan dunia nyata. Saya khususnya tertarik pada apa yang perlu Anda ketahui untuk mewakilinya dalam penggunaan fisik (misalnya, dengan dolar, Anda tidak pernah memiliki ketepatan kurang dari $ 0,01, yang memungkinkan representasi sebagai bilangan bulat sen).
Tetapi sulit untuk membuat asumsi tentang seberapa fleksibelnya program Anda ketika satu-satunya mata uang yang Anda tahu adalah mata uang barat yang populer (seperti dolar, euro, dan pound). Apa lagi pengetahuan yang relevan dalam perspektif yang murni terprogram? Saya tidak peduli dengan topik pertobatan.
Khususnya, apa yang perlu kita ketahui untuk dapat menyimpan nilai dalam beberapa mata uang dan mencetaknya?
Jawaban:
Oh benarkah?
Silakan menyimpan inci dalam nomor floating point IEEE 754 . Mereka menyimpan persis seperti yang Anda harapkan.
Jangan ragu untuk menyimpan sejumlah uang dalam nomor floating point IEEE 754 yang dapat Anda simpan menggunakan kutu yang membagi penggaris menjadi pecahan satu inci.
Mengapa? Karena ketika Anda menggunakan IEEE 754 itulah cara Anda menyimpannya.
Hal tentang inci adalah mereka dibagi menjadi dua. Hal tentang sebagian besar jenis mata uang adalah mereka dibagi dalam sepuluh (beberapa jenis tidak tetapi mari kita tetap fokus).
Perbedaan ini tidak akan terlalu membingungkan kecuali bahwa, untuk sebagian besar bahasa pemrograman, input dan output dari angka-angka floating point IEEE 754 dinyatakan dalam desimal! Yang sangat aneh karena mereka tidak disimpan dalam desimal.
Karena ini, Anda tidak pernah bisa melihat bagaimana bit melakukan hal-hal aneh ketika Anda meminta komputer untuk menyimpan
0.1
. Anda hanya melihat keanehan ketika Anda melakukan matematika melawannya dan itu memiliki kesalahan aneh.Dari java efektif Josh Bloch :
Menghasilkan
0.6100000000000001
Apa yang paling banyak diceritakan tentang ini bukanlah
1
cara duduk di sebelah kanan. Ini angka aneh yang harus digunakan untuk mendapatkannya. Daripada menggunakan contoh yang paling populer0.1
, kita harus menggunakan contoh yang menunjukkan masalah dan menghindari pembulatan yang akan menyembunyikannya.Misalnya, mengapa ini bekerja?
Menghasilkan
-0.01
Karena kita beruntung.
Saya benci masalah yang sulit didiagnosis karena kadang-kadang saya "beruntung".
IEEE 754 tidak bisa menyimpan 0,1 dengan tepat. Tetapi jika Anda memintanya untuk menyimpan 0,1 dan kemudian meminta untuk mencetak maka itu akan menampilkan 0,1 dan Anda akan berpikir semuanya baik-baik saja. Ini tidak baik, tetapi Anda tidak dapat melihat itu karena pembulatan untuk kembali ke 0,1.
Beberapa orang mengacaukan heck out dari orang lain dengan menyebut perbedaan ini untuk memperbaiki kesalahan. Tidak, ini bukan kesalahan pembulatan. Pembulatan adalah melakukan apa yang seharusnya dan mengubah apa yang bukan desimal menjadi desimal sehingga dapat dicetak di layar.
Tapi ini menyembunyikan ketidakcocokan antara bagaimana nomor ditampilkan dan bagaimana disimpan. Kesalahan tidak terjadi ketika pembulatan terjadi. Itu terjadi ketika Anda memutuskan untuk memasukkan nomor ke dalam sistem yang tidak dapat menyimpannya dengan tepat dan menganggap itu disimpan tepat ketika tidak.
Tidak ada yang mengharapkan π untuk menyimpan secara tepat dalam kalkulator dan mereka berhasil bekerja dengan baik. Jadi masalahnya bukan tentang presisi. Ini tentang presisi yang diharapkan. Komputer menampilkan sepersepuluh
0.1
sama dengan kalkulator kami, jadi kami berharap mereka menyimpan sepersepuluh dengan sempurna seperti yang dilakukan kalkulator kami. Mereka tidak melakukannya. Yang mengejutkan, karena komputer lebih mahal.Mari saya tunjukkan ketidakcocokan:
Perhatikan bahwa 1/2 dan 0,5 berbaris dengan sempurna. Tapi 0,1 tidak berbaris. Tentu Anda bisa lebih dekat jika Anda terus membaginya dengan 2 tetapi Anda tidak akan pernah memukulnya dengan tepat. Dan kita membutuhkan semakin banyak bit setiap kali kita bagi dengan 2. Jadi, mewakili 0,1 dengan sistem apa pun yang membaginya dengan 2 membutuhkan jumlah bit yang tak terbatas. Hard drive saya tidak sebesar itu.
Jadi IEEE 754 berhenti mencoba ketika kehabisan bit. Yang bagus karena saya perlu ruang di hard drive saya untuk ... foto keluarga. Tidak benar-benar. Foto keluarga. : P
Bagaimanapun, apa yang Anda ketikkan dan apa yang Anda lihat adalah desimal (di sebelah kanan) tetapi yang Anda simpan adalah bicimal (di sebelah kiri). Terkadang itu sama persis. Terkadang tidak. Terkadang sepertinya mereka sama, padahal sebenarnya tidak. Itulah pembulatannya.
Tolong, jika Anda menangani uang berbasis desimal saya, jangan gunakan pelampung atau ganda.
Jika Anda yakin hal-hal seperti sepersepuluh uang tidak akan terlibat maka simpan saja uang itu. Jika Anda tidak mencari tahu unit terkecil dari mata uang ini akan menjadi dan menggunakannya. Jika Anda tidak bisa, gunakan sesuatu seperti BigDecimal .
Kekayaan bersih saya mungkin akan selalu cocok dengan integer 64 bit, tetapi hal-hal seperti BigInteger bekerja dengan baik untuk proyek-proyek yang lebih besar dari itu. Mereka lebih lambat dari tipe asli.
Mencari tahu bagaimana menyimpannya hanya setengah dari masalah. Ingat Anda juga harus bisa menampilkannya. Desain yang baik akan memisahkan kedua hal ini. Masalah sebenarnya dengan menggunakan pelampung di sini adalah kedua hal itu disatukan.
sumber
"Perpustakaan" tidak perlu kecuali perpustakaan standar bahasa Anda kurang dalam tipe data tertentu, seperti yang akan saya jelaskan.
Sederhananya, Anda membutuhkan desimal titik tetap , bukan desimal titik mengambang . Misalnya, kelas BigDecimal Java dapat digunakan untuk menyimpan jumlah mata uang. Bahasa modern lainnya memiliki tipe bawaan yang serupa, termasuk C # dan Python . Implementasinya beragam, tetapi biasanya menyimpan angka sebagai bilangan bulat, dengan lokasi desimal sebagai anggota data yang terpisah. Ini memberikan presisi yang tepat, bahkan ketika melakukan aritmatika yang akan memberikan sisa aneh (misalnya 0,0000001) dengan angka-angka floating point IEEE.
Ada beberapa poin penting.
Gunakan tipe desimal yang sebenarnya daripada floating point.
Memahami bahwa jumlah mata uang memiliki dua komponen: nilai (5,63) dan kode atau jenis mata uang (USD, CAD, GBP, EUR, dkk). Kadang-kadang Anda dapat mengabaikan kode mata uang, di lain waktu itu sangat penting. Bagaimana jika Anda bekerja pada sistem keuangan atau ritel / e-commerce yang memungkinkan banyak mata uang? Apa yang terjadi jika Anda mencoba mengambil uang dari pelanggan dalam CAD, tetapi mereka ingin membayar dengan MXN? Anda memerlukan jenis "uang" dengan kode mata uang dan jumlah mata uang untuk dapat mencampur nilai-nilai ini (juga nilai tukar, tetapi saya tidak ingin terlalu jauh dengan garis singgung). Pada saat yang sama, perangkat lunak keuangan pribadi saya tidak perlu khawatir tentang ini karena semuanya dalam USD ( dapat mencampur mata uang, tetapi saya tidak pernah perlu).
Sementara mata uang mungkin memiliki unit fisik terkecil dalam praktiknya (CAD dan USD memiliki sen, JPY hanya ... Yen) dimungkinkan untuk menjadi lebih kecil. Jawaban CandiedOrange menunjukkan harga bahan bakar dalam sepersepuluh sen. Pajak properti saya dinilai sebagai pabrik per dolar, atau sepersepuluh sen (1/1000 dolar AS). Jangan batasi diri Anda hingga $ 0,01. Sementara Anda dapat menampilkan nilai-nilai itu sebagian besar waktu, jenis Anda harus memungkinkan lebih kecil (tipe desimal yang dirujuk di atas lakukan).
Perhitungan menengah tentunya harus memungkinkan lebih banyak presisi daripada satu sen. Saya telah bekerja pada sistem ritel / e-commerce di mana nilai-nilai internal dibulatkan menjadi $ 0,00000001 secara internal. Ketepatan tanpa batas biasanya tidak didukung oleh tipe desimal (atau SQL) sehingga harus ada batasan. Misalnya, membagi 1/3 menggunakan BigDecimal Java akan mengeluarkan pengecualian tanpa ditentukan RoundingMode atau MathContext karena nilainya tidak dapat direpresentasikan secara tepat.
Bagaimanapun, ini sangat penting dalam kasus-kasus tertentu. Mari kita asumsikan Anda memiliki pengguna dengan enam item di keranjang belanja, dan dia pergi untuk check out. Sistem Anda harus menghitung pajak, dan melakukannya per item karena item dapat dikenakan pajak berbeda. Jika Anda membulatkan pajak di setiap item, Anda mungkin mendapatkan kesalahan pembulatan sen pada tingkat transaksi / keranjang. Salah satu pendekatan untuk memperbaikinya adalah dengan menyimpan pajak ke lebih banyak tempat desimal per item, mendapatkan total untuk seluruh transaksi, dan kembali dan keliling setiap item sehingga pajak total benar (mungkin satu item dibulatkan ke atas, ke bawah lainnya).
Hal penting yang harus disadari dari semua ini adalah bahwa sesuatu yang sama pentingnya dengan pembulatan uang bisa sangat penting bagi orang yang tepat (misalnya, beberapa pelanggan saya di masa lalu yang harus membayar pajak penjualan pemerintah atas nama pelanggan mereka). Namun, ini semua adalah masalah yang diselesaikan. Ingatlah poin-poin di atas dan lakukan eksperimen sendiri, dan Anda akan belajar sambil melakukan.
sumber
Satu tempat di mana banyak pengembang dihadapkan dengan representasi data tunggal untuk mata uang apa pun adalah untuk pembelian dalam aplikasi untuk aplikasi iOS. Pelanggan Anda dapat terhubung ke toko di hampir semua negara di dunia. Dan dalam situasi itu, Anda akan diberi harga pembelian yang terdiri dari angka presisi ganda , dan kode mata uang.
Anda harus sadar bahwa jumlahnya bisa besar. Ada mata uang di mana setara dengan katakanlah sepuluh dolar lebih dari 100.000 unit. Dan kami beruntung bahwa tidak ada mata uang seperti dolar Zimbabwe saat ini, di mana Anda dapat memiliki seratus triliun uang kertas!
Untuk menampilkan mata uang, Anda akan memerlukan beberapa perpustakaan - Anda tidak memiliki kesempatan untuk memperbaikinya sendiri. Tampilan tergantung pada dua hal: Kode mata uang, dan lokal pengguna. Pikirkan bagaimana dolar AS dan dolar Kanada akan ditampilkan dengan lokal AS dan lokal Kanada: Di AS, Anda memiliki $ vs CAN $, dan di Kanada Anda memiliki US $ vs $. Harapan yang dibangun ke dalam OS, atau Anda memiliki perpustakaan yang bagus.
Untuk perhitungan, perhitungan apa pun akan berakhir dengan langkah pembulatan. Anda harus mengetahui bagaimana Anda harus melakukan pembulatan itu secara legal . Itu bukan masalah pemrograman, itu masalah hukum. Misalnya, jika Anda menghitung PPN di Inggris, Anda harus menghitung pajak per item atau per baris item, dan membulatkannya menjadi uang. Apa yang Anda bulatkan tergantung pada mata uang. Tapi aturannya jelas tergantung pada negara. Anda tidak dapat mengharapkan bahwa perhitungan yang secara hukum benar di Inggris akan benar secara hukum di Jepang dan sebaliknya.
sumber
Beberapa contoh masalah yang terkait dengan lokal:
Masalah potensial lainnya adalah bahkan jika Anda menyimpan mata uang dengan benar dalam angka titik tetap, mungkin perlu dikonversi ke angka titik mengambang karena perpustakaan yang digunakan untuk mencetak hanya mendukung titik mengambang.
sumber