public class doublePrecision {
public static void main(String[] args) {
double total = 0;
total += 5.6;
total += 5.8;
System.out.println(total);
}
}
Kode di atas mencetak:
11.399999999999
Bagaimana saya bisa mencetak ini (atau bisa menggunakannya) 11.4?
java
floating-point
double
precision
Deinumite
sumber
sumber
Jawaban:
Seperti yang disebutkan orang lain, Anda mungkin ingin menggunakan
BigDecimal
kelas, jika Anda ingin memiliki representasi 11.4.Sekarang, sedikit penjelasan mengapa ini terjadi:
The
float
dandouble
primitif jenis di Jawa floating point nomor, di mana jumlah disimpan sebagai representasi biner dari fraksi dan eksponen a.Lebih khusus lagi, nilai floating point presisi ganda seperti
double
jenisnya adalah nilai 64-bit, di mana:Bagian-bagian ini digabungkan untuk menghasilkan
double
representasi suatu nilai.(Sumber: Wikipedia: Presisi ganda )
Untuk uraian terperinci tentang bagaimana nilai-nilai titik mengambang ditangani di Jawa, lihat Bagian 4.2.3: Jenis Titik Apung, Format, dan Nilai Spesifikasi Bahasa Jawa.
The
byte
,char
,int
,long
jenis yang fixed-point nomor, yang representions yang tepat dari angka. Tidak seperti angka titik tetap, angka titik mengambang akan beberapa kali (aman untuk menganggap "sebagian besar waktu") tidak dapat mengembalikan representasi angka yang tepat. Ini adalah alasan mengapa Anda berakhir dengan11.399999999999
sebagai hasil dari5.6 + 5.8
.Saat membutuhkan nilai yang tepat, seperti 1.5 atau 150.1005, Anda akan ingin menggunakan salah satu tipe titik tetap, yang akan dapat mewakili angka dengan tepat.
Seperti yang telah disebutkan beberapa kali, Java memiliki a
BigDecimal
kelas yang akan menangani jumlah yang sangat besar dan jumlah yang sangat kecil.Dari Referensi Java API untuk
BigDecimal
kelas:Ada banyak pertanyaan tentang Stack Overflow terkait dengan masalah angka floating point dan ketepatannya. Berikut adalah daftar pertanyaan terkait yang mungkin menarik:
Jika Anda benar-benar ingin pergi ke detail seluk beluk nomor floating point, lihat Apa Yang Setiap Ilmuwan Komputer Harus Tahu Tentang Aritmatika Floating-Point .
sumber
BigDecimal
jauh lebih lambat daripadadouble
dalam kasus ini tidak diperlukan karena ganda memiliki 15 tempat desimal akurasi, Anda hanya perlu pembulatan.Ketika Anda memasukkan angka ganda, misalnya,
33.33333333333333
nilai yang Anda dapatkan sebenarnya adalah nilai presisi ganda yang dapat direpresentasikan, yaitu:Membagi dengan 100 memberi:
yang juga tidak dapat direpresentasikan sebagai angka presisi ganda, jadi sekali lagi itu dibulatkan ke nilai keterwakilan terdekat, yaitu:
Ketika Anda mencetak nilai ini, itu akan dibulatkan lagi menjadi 17 angka desimal, memberikan:
sumber
Jika Anda hanya ingin memproses nilai sebagai pecahan, Anda bisa membuat kelas pecahan yang berisi bidang pembilang dan penyebut.
Tulis metode untuk menambah, mengurangi, mengalikan, dan membagi serta metode toDouble. Dengan cara ini Anda dapat menghindari mengapung saat perhitungan.
EDIT: Implementasi cepat,
sumber
numerator
dandenominator
harusint
s? Mengapa Anda ingin presisi floating-point?Perhatikan bahwa Anda akan memiliki masalah yang sama jika Anda menggunakan aritmatika desimal presisi-terbatas, dan ingin berurusan dengan 1/3: 0,333333333 * 3 adalah 0,999999999, bukan 1,00000000.
Sayangnya, 5.6, 5.8 dan 11.4 hanya bukan angka bulat dalam biner, karena mereka melibatkan perlima. Jadi representasi float dari mereka tidak tepat, sama seperti 0,3333 tidak tepat 1/3.
Jika semua angka yang Anda gunakan adalah desimal yang tidak berulang, dan Anda menginginkan hasil yang pasti, gunakan BigDecimal. Atau seperti yang dikatakan orang lain, jika nilai Anda seperti uang dalam arti bahwa mereka semua kelipatan 0,01, atau 0,001, atau sesuatu, maka gandakan semuanya dengan kekuatan tetap 10 dan gunakan int atau panjang (penambahan dan pengurangan adalah sepele: hati-hati terhadap multiplikasi).
Namun, jika Anda senang dengan perhitungan biner, tetapi Anda hanya ingin mencetak beberapa hal dalam format yang sedikit lebih ramah, coba
java.util.Formatter
atauString.format
. Dalam format string, tentukan presisi kurang dari ketepatan penuh ganda. Untuk 10 angka penting, katakanlah, 11.399999999999 adalah 11,4, sehingga hasilnya akan hampir seakurat dan lebih dapat dibaca manusia dalam kasus di mana hasil biner sangat dekat dengan nilai yang hanya membutuhkan beberapa tempat desimal.Ketepatan untuk menentukan sedikit tergantung pada berapa banyak matematika yang telah Anda lakukan dengan angka-angka Anda - secara umum semakin banyak Anda lakukan, semakin banyak kesalahan akan terakumulasi, tetapi beberapa algoritma menumpuknya lebih cepat daripada yang lain (mereka disebut "tidak stabil" sebagai menentang "stable" sehubungan dengan kesalahan pembulatan). Jika semua yang Anda lakukan adalah menambahkan beberapa nilai, maka saya kira bahwa menjatuhkan hanya satu tempat desimal presisi akan menyelesaikan masalah. Percobaan.
sumber
Anda mungkin ingin melihat menggunakan kelas java.math.BigDecimal java jika Anda benar-benar membutuhkan matematika presisi. Berikut ini adalah artikel bagus dari Oracle / Sun tentang kasus untuk BigDecimal . Meskipun Anda tidak pernah bisa mewakili 1/3 sebagai seseorang yang disebutkan, Anda bisa memiliki kekuatan untuk memutuskan secara tepat seberapa Anda inginkan hasilnya. setScale () adalah teman Anda .. :)
Ok, karena saya punya terlalu banyak waktu di tangan saya saat ini di sini adalah contoh kode yang berkaitan dengan pertanyaan Anda:
dan untuk memasukkan bahasa favorit baru saya, Groovy, berikut adalah contoh yang lebih rapi dari hal yang sama:
sumber
Cukup yakin Anda bisa membuatnya menjadi contoh tiga baris. :)
Jika Anda ingin presisi yang tepat, gunakan BigDecimal. Jika tidak, Anda dapat menggunakan int dikalikan dengan 10 ^ presisi apa pun yang Anda inginkan.
sumber
Seperti yang telah dicatat oleh orang lain, tidak semua nilai desimal dapat direpresentasikan sebagai biner karena desimal didasarkan pada pangkat 10 dan biner didasarkan pada pangkat dua.
Jika masalah presisi, gunakan BigDecimal, tetapi jika Anda hanya ingin hasil yang ramah:
Akan memberimu:
sumber
Anda menjalankan pembatasan ketepatan tipe ganda.
Java.Math memiliki beberapa fasilitas aritmatika presisi arbitrer.
sumber
Anda tidak bisa, karena 7.3 tidak memiliki representasi terbatas dalam biner. Yang terdekat yang bisa Anda dapatkan adalah 2054767329987789/2 ** 48 = 7.3 + 1/1407374883553280.
Lihatlah http://docs.python.org/tutorial/floatingpoint.html untuk penjelasan lebih lanjut. (Ada di situs web Python, tetapi Java dan C ++ memiliki "masalah" yang sama.)
Solusinya tergantung pada apa sebenarnya masalah Anda:
sumber
sumber
Gunakan java.math.BigDecimal
Doubles adalah fraksi biner secara internal, sehingga mereka terkadang tidak dapat merepresentasikan fraksi desimal ke desimal yang tepat.
sumber
Lipat gandakan semuanya menjadi 100 dan simpan dalam sen.
sumber
Komputer menyimpan angka dalam biner dan tidak bisa benar-benar mewakili angka seperti 33.333333333 atau 100.0 tepatnya. Ini adalah salah satu hal rumit tentang menggunakan ganda. Anda harus memutari jawaban sebelum menunjukkannya kepada pengguna. Untungnya di sebagian besar aplikasi, Anda tidak perlu banyak tempat desimal.
sumber
Angka floating point berbeda dari bilangan real karena untuk setiap angka floating point yang diberikan ada angka floating point berikutnya yang lebih tinggi. Sama seperti bilangan bulat. Tidak ada bilangan bulat antara 1 dan 2.
Tidak ada cara untuk mewakili 1/3 sebagai pelampung. Ada pelampung di bawahnya dan ada pelampung di atasnya, dan ada jarak tertentu di antara mereka. Dan 1/3 ada di ruang itu.
Apfloat untuk Java mengklaim dapat bekerja dengan angka floating point presisi yang sewenang-wenang, tetapi saya tidak pernah menggunakannya. Mungkin patut dilihat. http://www.apfloat.org/apfloat_java/
Pertanyaan serupa diajukan di sini sebelum Java floating point perpustakaan presisi tinggi
sumber
Doubles adalah perkiraan angka desimal di sumber Java Anda. Anda melihat konsekuensi dari ketidakcocokan antara ganda (yang merupakan nilai kode biner) dan sumber Anda (yang kode desimal).
Java memproduksi perkiraan biner terdekat. Anda dapat menggunakan java.text.DecimalFormat untuk menampilkan nilai desimal yang terlihat lebih baik.
sumber
Gunakan BigDecimal. Bahkan memungkinkan Anda menentukan aturan pembulatan (seperti ROUND_HALF_EVEN, yang akan meminimalkan kesalahan statistik dengan membulatkan ke tetangga genap jika keduanya memiliki jarak yang sama; yaitu putaran 1,5 dan 2,5 ke 2).
sumber
Jawaban singkat: Selalu gunakan BigDecimal dan pastikan Anda menggunakan konstruktor dengan String argumen , bukan yang ganda.
Kembali ke contoh Anda, kode berikut akan mencetak 11.4, sesuai keinginan.
sumber
Lihat BigDecimal, ia menangani masalah berurusan dengan aritmatika floating point seperti itu.
Panggilan baru akan terlihat seperti ini:
Gunakan setScale () untuk mengatur jumlah presisi tempat desimal yang akan digunakan.
sumber
Mengapa tidak menggunakan metode round () dari kelas Math?
sumber
Jika Anda tidak punya pilihan selain menggunakan nilai ganda, dapat menggunakan kode di bawah ini.
sumber
Jangan sia-siakan usaha Anda menggunakan BigDecimal. Dalam 99,99999% kasus Anda tidak membutuhkannya. tipe java ganda adalah perkiraan kasar tetapi dalam hampir semua kasus, itu cukup tepat. Ingatlah bahwa Anda memiliki kesalahan pada digit signifikan ke-14.Ini benar-benar dapat diabaikan!
Untuk mendapatkan hasil yang bagus gunakan:
sumber