Dalam permainan simulasi bisnis sederhana (dibangun di Java + Slick2D), haruskah jumlah uang pemain saat ini disimpan sebagai float
atau int
, atau sesuatu yang lain?
Dalam kasus penggunaan saya, sebagian besar transaksi akan menggunakan sen ($ 0,50, $ 1,20, dll), dan perhitungan suku bunga sederhana akan terlibat.
Saya telah melihat orang-orang mengatakan Anda tidak boleh menggunakan float
mata uang, serta orang-orang mengatakan Anda tidak boleh menggunakan int
mata uang. Saya merasa harus menggunakan int
dan menyelesaikan perhitungan persentase yang diperlukan. Apa yang harus saya gunakan?
Currency
tipe seperti Delphi, yang menggunakan matematika titik tetap skala untuk memberi Anda matematika desimal tanpa masalah presisi yang melekat pada titik mengambang?BigDecimal
masalah seperti ini.Jawaban:
Anda dapat menggunakan
int
, dan mempertimbangkan semuanya dalam sen. $ 1,20 hanya 120 sen. Pada layar, Anda meletakkan desimal di tempatnya.Perhitungan bunga hanya akan dipotong atau dibulatkan. Begitu
Dengan cara ini, Anda tidak memiliki desimal acak yang selalu menempel. Anda bisa menjadi kaya dengan menambahkan uang yang tidak terhitung (karena pembulatan) ke dalam rekening bank Anda sendiri
sumber
round
tidak ada alasan nyata untuk dilemparkanint
, bukan?Oke, saya akan melompat.
Saran saya: ini permainan. Tenang dan gunakan
double
.Inilah alasan saya:
float
memang memiliki masalah presisi yang muncul ketika menambahkan unit ke jutaan, jadi meskipun mungkin jinak, saya akan menghindari tipe itu.double
hanya mulai mendapatkan masalah di sekitar kuintillon (satu miliar miliar).int
danlong
tidak berguna karena Anda tidak tahu harus berhenti di mana dengan desimal.BigDecimal
akan memberi Anda presisi sewenang-wenang tetapi akan menjadi sangat lambat, kecuali jika Anda menjepit presisi pada suatu saat. Apa aturan pembulatannya? Bagaimana Anda memilih tempat untuk berhenti? Apakah layak memiliki bit presisi lebih banyak daripadadouble
? Saya percaya tidak.Alasan fixed point aritmatika digunakan dalam aplikasi keuangan adalah karena mereka bersifat deterministik. Aturan pembulatan didefinisikan dengan sempurna, kadang-kadang oleh hukum, dan harus diterapkan secara ketat, tetapi pembulatan masih terjadi di beberapa titik. Argumen apa pun yang mendukung tipe tertentu berdasarkan presisi cenderung palsu. Semua tipe memiliki masalah presisi dengan jenis perhitungan yang akan Anda lakukan.
Contoh-contoh praktis
Saya melihat beberapa komentar mengklaim hal-hal tentang pembulatan atau ketepatan yang saya tidak setuju. Berikut adalah beberapa contoh tambahan untuk menggambarkan apa yang saya maksud.
Menyimpan : jika unit dasar Anda adalah cent, Anda mungkin ingin membulatkan ke cent terdekat saat menyimpan nilai:
Anda tidak akan mendapatkan masalah pembulatan saat mengadopsi metode ini yang tidak akan Anda miliki dengan tipe integer.
Mengambil : semua perhitungan dapat dilakukan secara langsung pada nilai ganda, tanpa konversi:
Perhatikan bahwa kode di atas tidak berfungsi jika Anda ganti
double
denganint64_t
: akan ada konversi implisit kedouble
, kemudian pemotongan keint64_t
, dengan kemungkinan hilangnya informasi.data.GetValue ()Membandingkan : perbandingan adalah satu hal yang harus dilakukan dengan tipe floating-point. Saya sarankan menggunakan metode perbandingan seperti ini:
Membulatkan keadilan
Misalkan Anda memiliki $ 9,99 dalam akun dengan bunga 4%. Berapa banyak yang pemain dapat hasilkan? Dengan pembulatan integer Anda mendapatkan $ 0,03; dengan pembulatan floating-point Anda mendapatkan $ 0,04. Saya percaya yang terakhir ini lebih adil.
sumber
int_64t
s.10/3 = 3+3+4
atau10/3 = 3+3+3 (+1)
. Untuk semua operasi lainnya, bilangan bulat bekerja dengan sempurna, tidak seperti floating point yang bisa mendapatkan masalah pembulatan dalam operasi apa pun.Jenis titik apung di Jawa (
float
,double
) bukan representasi yang baik untuk mata uang karena satu alasan utama - ada kesalahan mesin dalam pembulatan. Bahkan jika perhitungan sederhana mengembalikan seluruh nomor - seperti12.0/2
(6.0), floating point mungkin salah bulat itu (karena tho representasi spesifik jenis dalam memori) sebagai6.0000000000001
atau5.999999999999998
atau mirip. Ini adalah hasil dari pembulatan mesin spesifik yang terjadi pada prosesor dan unik untuk komputer yang menghitungnya. Biasanya, jarang ada masalah untuk beroperasi dengan nilai-nilai ini, karena kesalahannya cukup lalai, tetapi sulit untuk menampilkannya kepada pengguna.Solusi yang mungkin untuk ini adalah dengan menggunakan implementasi kustom tipe data floating point, seperti
BigDecimal
. Ini mendukung mekanisme perhitungan yang lebih baik yang setidaknya mengisolasi kesalahan pembulatan bukan untuk mesin spesifik, tetapi lebih lambat dalam hal kinerja.Jika Anda membutuhkan produktivitas tinggi, Anda sebaiknya tetap menggunakan tipe yang sederhana. Jika Anda beroperasi dengan data keuangan penting , dan setiap sen penting (seperti aplikasi Forex, atau permainan kasino) maka saya sarankan Anda untuk menggunakan
Long
ataulong
.Long
akan memungkinkan Anda untuk menangani jumlah besar dan presisi yang baik. Anggap saja Anda membutuhkan, katakanlah, 4 digit setelah titik desimal, yang Anda butuhkan adalah mengalikan jumlahnya dengan 10.000. Memiliki pengalaman dalam mengembangkan game kasino online, sayaLong
sering terlihat sering mewakili uang dalam sen . Dalam aplikasi Forex, ketepatan lebih penting, sehingga Anda akan membutuhkan pengali yang lebih besar - tetap saja, bilangan bulat bebas dari masalah pembulatan mesin (tentu saja pembulatan manual seperti pada 3/2 yang harus Anda tangani sendiri).Pilihan yang dapat diterima adalah menggunakan tipe floating point standar -
Float
danDouble
, jika kinerja lebih penting daripada akurasi hingga ratusan persen. Kemudian, pada logika tampilan Anda, yang Anda butuhkan adalah menggunakan pemformatan yang telah ditentukan , sehingga keburukan dari pembulatan mesin potensial tidak sampai ke pengguna.sumber
Untuk game skala kecil dan di mana kecepatan proses, memori adalah masalah penting (karena presisi atau bekerja dengan co-prosesor matematika dapat membuat sangat lambat), ada dua kali lipat sudah cukup.
Tetapi untuk game skala besar (misalnya, game sosial) dan di mana kecepatan proses, memori tidak terbatas, ada BigDecimal lebih baik. Karena di sini,
Sumber:
Dari https://stackoverflow.com/questions/3730019/why-not-use-double-or-float-to-represent-currency
Dari Bloch, J., Java Efektif, edisi ke-2, Item 48:
Lihat juga
sumber
Anda ingin menyimpan mata uang Anda
long
dan menghitung mata uang Andadouble
, setidaknya sebagai cadangan. Anda ingin semua transaksi terjadi sebagailong
.Alasan Anda ingin menyimpan mata uang Anda
long
adalah karena Anda tidak ingin kehilangan mata uang apa pun.Misalkan Anda menggunakan
double
, dan Anda tidak punya uang. Seseorang memberi Anda tiga sen, lalu membawanya kembali.Yah, itu tidak terlalu keren. Mungkin seseorang dengan $ 10 ingin memberikan kekayaannya dengan terlebih dahulu memberi Anda tiga sen, dan kemudian memberikan $ 9,70 kepada orang lain.
Dan kemudian Anda memberi mereka uang receh kembali:
Ini baru saja rusak.
Sekarang, mari kita gunakan yang panjang, dan kami akan melacak sepersepuluh sen (jadi 1 = $ 0,001). Mari kita beri setiap orang di planet ini satu miliar, seratus dua belas juta, tujuh puluh lima ribu, seratus empat puluh tiga dolar:
Um, tunggu, kita bisa memberi semua orang lebih dari satu miliar dolar, dan hanya menghabiskan sedikit lebih dari dua? Overflow adalah bencana di sini.
Jadi, setiap kali Anda menghitung jumlah uang yang akan ditransfer, gunakan
double
danMath.round
itu untuk mendapatkanlong
. Kemudian perbaiki saldo (tambahkan dan kurangi kedua akun) menggunakanlong
.Ekonomi Anda tidak akan bocor, dan akan meningkat hingga empat triliun dolar.
Ada masalah yang lebih rumit - misalnya, apa yang Anda lakukan jika Anda melakukan dua puluh pembayaran? * - tetapi ini harus Anda mulai.
* Anda menghitung apa satu pembayaran, bulat ke
long
; kemudian kalikan dengan20.0
dan periksa apakah itu dalam jangkauan; jika demikian, Anda melipatgandakan pembayaran dengan20L
untuk mendapatkan jumlah yang dikurangkan dari saldo Anda. Secara umum, semua transaksi harus ditanganilong
, jadi Anda benar-benar perlu meringkas semua transaksi individu; Anda dapat mengalikannya sebagai pintasan, tetapi Anda harus memastikan bahwa Anda tidak menambahkan kesalahan pembulatan dan bahwa Anda tidak meluap, yang berarti Anda perlu memeriksadouble
sebelum melakukan perhitungan nyata denganlong
.sumber
Saya akan mengatakan bahwa nilai apa pun yang dapat ditampilkan kepada pengguna hampir selalu berupa bilangan bulat. Uang hanyalah contoh paling menonjol dari ini. Menangani 225 kerusakan empat kali pada monster dengan 900 HP dan menemukan bahwa itu masih memiliki 1 HP tersisa akan mengurangi pengalaman sama seperti menemukan bahwa Anda adalah fraksi tak terlihat dari satu sen pendek kekurangan sesuatu yang memberikan sesuatu.
Di sisi yang lebih teknis saya pikir perlu dicatat bahwa seseorang tidak harus kembali ke pelampung untuk melakukan hal-hal canggih seperti minat. Selama Anda memiliki cukup ruang kepala di bilangan bulat yang Anda pilih, perkalian dan sebuah divisi akan bertahan untuk perkalian dengan angka desimal, misalnya untuk menambah 4%, dibulatkan ke bawah:
Untuk menambah 4%, dibulatkan oleh konvensi standar:
Tidak ada titik float yang tidak akurat di sini, pembulatan selalu terbelah tepat pada
.5
sasaran.Edit, pertanyaan sebenarnya:
Melihat bagaimana perdebatan telah pergi aku mulai berpikir bahwa menguraikan apa pertanyaannya adalah semua tentang mungkin lebih berguna daripada sederhana
int
/float
jawaban. Inti dari pertanyaan ini bukan tentang tipe data, ini tentang mengendalikan detail suatu program.Menggunakan integer untuk mewakili nilai non-integer memaksa programmer untuk berurusan dengan detail implementasi. "Apa presisi yang digunakan?" dan "Cara apa untuk berputar?" adalah pertanyaan yang harus dijawab secara eksplisit.
Melayang di sisi lain tidak memaksa programmer untuk khawatir, itu sudah cukup banyak melakukan apa yang diharapkan. Tetapi karena pelampung bukanlah presisi yang tidak terbatas, beberapa pembulatan akan terjadi, dan pembulatan itu sangat tidak terduga.
Apa yang terjadi dalam sekali pakai mengapung dan ingin mengendalikan pembulatan? Ternyata hampir mustahil. Satu-satunya cara untuk membuat float benar-benar dapat diprediksi adalah dengan hanya menggunakan nilai-nilai yang dapat direpresentasikan secara keseluruhan
2^n
. Tapi konstruksi itu membuat pelampung cukup sulit untuk digunakan.Jadi jawaban untuk pertanyaan sederhana adalah: Jika Anda ingin mengambil kendali, gunakan bilangan bulat, jika tidak, gunakan float.
Tetapi pertanyaan yang sedang diperdebatkan hanyalah bentuk lain dari pertanyaan: Apakah Anda ingin mengambil kendali?
sumber
Bahkan jika itu "hanya permainan", saya akan menggunakan Pola Uang dari Martin Fowler, didukung oleh lama.
Mengapa?
Lokalisasi (L10n): Menggunakan pola itu Anda dapat dengan mudah melokalkan mata uang game Anda. Pikirkan tentang game taipan lama seperti "Transport Tycoon". Mereka dengan mudah memungkinkan pemain untuk mengubah mata uang dalam game (yaitu Dari Pound Inggris ke Dolar AS) untuk memenuhi mata uang dunia nyata.
Dan
Itu berarti Anda dapat menyimpan 9000 kali Pasokan Uang M2 AS saat ini (~ 10.000 Miliar Dolar). Memberi Anda cukup ruang untuk menggunakan mata uang dunia lainnya, mungkin, bahkan mereka yang pernah / memiliki hiperinflasi (Jika penasaran, lihat inflasi Jerman pasca Perang Dunia I , di mana 1 pon roti adalah 3.000.000.000 mark)
Panjang mudah untuk bertahan, sangat cepat dan harus memberi Anda ruang yang cukup untuk melakukan semua perhitungan bunga hanya menggunakan integer arithmetics, jawaban eBusiness memberikan penjelasan tentang cara melakukannya.
sumber
Berapa banyak pekerjaan yang ingin Anda lakukan dalam hal ini? Seberapa penting akurasi? Apakah Anda peduli melacak kesalahan fraksional yang terjadi dengan pembulatan dan ketidaktepatan mewakili angka desimal dalam sistem biner?
Pada akhirnya saya cenderung menghabiskan sedikit lebih banyak waktu pengkodean dan mengimplementasikan unit test untuk "kasus sudut" dan kasus bermasalah yang diketahui - jadi saya akan berpikir dalam hal BigInteger yang dimodifikasi yang mencakup jumlah besar secara sewenang-wenang dan mempertahankan bit fraksional dengan BigDecimal atau bagian BigRational (dua BigIntegers, satu untuk penyebut dan yang lain untuk pembilang) - dan termasuk kode untuk menjaga bagian pecahan menjadi fraksi yang sebenarnya dengan (mungkin hanya secara berkala) menambahkan bagian non-fraksional ke BigInteger utama. Lalu saya akan secara internal melacak segala sesuatu dalam sen hanya untuk menjaga bagian-bagian pecahan dari perhitungan GUI.
Mungkin cara kompleks untuk gim (sederhana) - tetapi bagus untuk perpustakaan yang diterbitkan sebagai sumber terbuka! Hanya perlu mencari cara untuk menjaga kinerja yang baik ketika berhadapan dengan bit fraksional ...
sumber
Tentu tidak melayang. Dengan hanya 7 digit, Anda hanya memiliki akurasi seperti:
12,345,67 123,456.7x
Jadi Anda tahu, sudah dengan 10 ^ 5 dolar, Anda kehilangan jejak uang. ganda dapat digunakan untuk tujuan game, bukan dalam kehidupan nyata karena masalah akurasi.
Tapi panjang (dan maksud saya ini 64 bit, atau panjang dalam C ++) tidak cukup jika Anda akan melacak transaksi dan menjumlahkannya. Itu cukup untuk mempertahankan kepemilikan bersih perusahaan mana pun, tetapi semua transaksi selama setahun bisa meluap. Itu tergantung pada seberapa besar "dunia" finansial Anda dalam permainan.
sumber
Solusi lain yang akan saya tambahkan di sini adalah membuat kelas uang. Kelasnya akan seperti (bahkan bisa menjadi struct).
Ini akan memungkinkan Anda untuk mewakili sen dalam hal ini sebagai bilangan bulat dan seluruh dolar sebagai bilangan bulat. Karena Anda memiliki panji terpisah untuk menjadi negatif, Anda dapat menggunakan bilangan bulat tidak bertanda untuk nilai-nilai Anda yang menggandakan jumlah yang mungkin (Anda juga bisa menulis kelas angka yang menerapkan ide ini ke angka untuk mendapatkan angka yang BENAR-BENAR besar). Yang perlu Anda lakukan adalah membebani operator matematika dan Anda bisa menggunakan ini seperti tipe data lama. Membutuhkan lebih banyak memori, tetapi benar-benar memperluas batas nilai.
Ini lebih lanjut dapat diperluas untuk mencakup hal-hal seperti jumlah unit parsial per seluruh unit sehingga Anda dapat memiliki mata uang yang membagi sesuatu selain 100 sub unit, sen dalam hal ini, per dolar. Misalnya, Anda dapat membuat 125 floopies untuk membuat satu flooper.
Sunting:
Saya akan memperluas gagasan bahwa Anda juga dapat melihat daftar jenis, yang dapat diakses oleh kelas uang yang akan memberikan nilai tukar untuk mata uangnya versus semua mata uang lainnya. Anda dapat membuat ini menjadi fungsi operator yang berlebihan. Karena itu, jika Anda secara otomatis mencoba menambahkan 4 USD ke 4 poundsterling Inggris, itu akan secara otomatis memberi Anda 6,6 pound (pada saat penulisan).
sumber
Sebagaimana disebutkan dalam salah satu komentar pada pertanyaan awal Anda, masalah ini telah dibahas di StackExchange: https://stackoverflow.com/questions/8148684/what-is-the-best-data-type-to-use-for- uang-dalam-aplikasi java
Pada dasarnya, tipe floating point tidak boleh digunakan untuk mewakili mata uang, dan seperti kebanyakan bahasa, Java memiliki tipe yang lebih tepat. Dokumentasi Oracle tentang primitif menyarankan penggunaan BigDecimal .
sumber
Gunakan kelas itu cara terbaik. Anda dapat menyembunyikan implementasi uang Anda, Anda dapat mengganti dua kali / panjang apa pun yang Anda inginkan setiap saat.
Contoh
Dalam penggunaan kode Anda cukup rajin giat itu saya pikir dan Anda dapat menyimpan metode yang lebih berguna.
sumber