Kapan saya harus menggunakan ganda daripada desimal?

265

Saya dapat menyebutkan tiga keuntungan menggunakan double(atau float) alih-alih decimal:

  1. Menggunakan lebih sedikit memori.
  2. Lebih cepat karena operasi matematika floating point secara asli didukung oleh prosesor.
  3. Dapat mewakili rentang angka yang lebih besar.

Tetapi keuntungan ini tampaknya hanya berlaku untuk operasi perhitungan yang intensif, seperti yang ditemukan dalam perangkat lunak pemodelan. Tentu saja, ganda tidak boleh digunakan ketika presisi dibutuhkan, seperti perhitungan keuangan. Jadi, adakah alasan praktis untuk memilih double(atau float) alih-alih decimaldalam aplikasi "normal"?

Diedit untuk menambahkan: Terima kasih atas semua tanggapan hebat, saya belajar dari mereka.

Satu pertanyaan lebih lanjut: Beberapa orang menyatakan bahwa ganda dapat lebih tepatnya mewakili bilangan real. Ketika dideklarasikan, saya akan berpikir bahwa mereka biasanya mewakili mereka dengan lebih akurat. Tetapi apakah benar pernyataan bahwa keakuratan dapat menurun (kadang-kadang secara signifikan) ketika operasi floating point dilakukan?

Ide Jamie
sumber
lihat juga stackoverflow.com/questions/2545567/…
Ian Ringrose
5
Ini terangkat cukup teratur dan saya masih berjuang dengan itu. Sebagai contoh, saya sedang mengerjakan aplikasi yang melakukan perhitungan finansial jadi saya menggunakan desimal secara keseluruhan. Tapi Matematika dan VisualBasic. Fungsi keuangan menggunakan ganda jadi ada banyak konversi yang membuat saya terus-menerus menebak penggunaan desimal.
Jamie Ide
@ JamieIde itu gila fungsi Keuangan menggunakan ganda, uang harus selalu dalam desimal.
Chris Marisic
@ ChrisMarisic Tapi apa yang bisa dilakukan jamie Ide bekerja dengan warisan lama menggunakan ganda? Maka Anda harus menggunakan ganda juga karena banyak konversi akan menyebabkan kesalahan pembulatan ... tidak heran dia menyebutkan VisualBasic pfffhh .....
Elisabeth
@ Elisabeth saya kemungkinan akan menggunakan pustaka yang berbeda yang mendukung desimal. Apa pun yang disediakan VisualBasic. Keuangan kemungkinan ada di beberapa perpustakaan lain hari ini
Chris Marisic

Jawaban:

306

Saya pikir Anda sudah merangkum keuntungan dengan cukup baik. Namun Anda kehilangan satu poin. The decimaljenis ini hanya lebih akurat di mewakili basis 10 angka (misalnya yang digunakan dalam mata uang / perhitungan keuangan). Secara umum, doublejenis ini akan menawarkan setidaknya sebagai presisi besar (seseorang mengoreksi saya jika saya salah) dan pasti kecepatan yang lebih besar untuk bilangan real arbitrer. Kesimpulan sederhananya adalah: ketika mempertimbangkan mana yang akan digunakan, selalu gunakan doublekecuali Anda membutuhkan base 10akurasi yang decimalmenawarkan.

Edit:

Mengenai pertanyaan tambahan Anda tentang penurunan akurasi angka floating-point setelah operasi, ini adalah masalah yang sedikit lebih halus. Memang, presisi (saya menggunakan istilah yang dipertukarkan untuk keakuratan di sini) akan terus menurun setelah setiap operasi dilakukan. Ini karena dua alasan:

  1. fakta bahwa angka-angka tertentu (paling jelas desimal) tidak dapat benar-benar diwakili dalam bentuk floating point
  2. kesalahan pembulatan terjadi, sama seperti jika Anda melakukan perhitungan dengan tangan. Ini sangat tergantung pada konteks (berapa banyak operasi yang Anda lakukan) apakah kesalahan ini cukup signifikan untuk menjamin banyak pemikiran.

Dalam semua kasus, jika Anda ingin membandingkan dua angka floating-point yang seharusnya secara teori setara (tetapi diterima dengan menggunakan perhitungan yang berbeda), Anda perlu mengizinkan tingkat toleransi tertentu (berapa banyak bervariasi, tetapi biasanya sangat kecil) .

Untuk tinjauan umum yang lebih rinci tentang kasus-kasus tertentu di mana kesalahan dalam akurasi dapat diperkenalkan, lihat bagian Akurasi artikel Wikipedia . Akhirnya, jika Anda menginginkan diskusi yang serius (dan matematis) tentang angka / operasi floating-point di tingkat mesin, coba baca artikel yang sering dikutip Apa Yang Harus Diketahui Setiap Ilmuwan Komputer Tentang Aritmatika Titik Apung .

Noldorin
sumber
1
Bisakah Anda memberikan contoh, dari nomor 10 basis dengan presisi yang hilang ketika mengkonversi ke basis 2?
Mark Cidade
@ Mark: 1.000001 adalah salah satu contoh, setidaknya menurut Jon Skeet. (Lihat pertanyaan 3 dari halaman ini: yoda.arachsys.com/csharp/teasers-answers.html )
Noldorin
25
@ Mark: contoh yang sangat sederhana: 0,1 adalah fraksi periodik dalam basis 2 sehingga tidak dapat dinyatakan secara tepat dalam a double. Komputer modern masih akan mencetak nilai yang benar tetapi hanya karena mereka "menebak" hasilnya - bukan karena itu benar-benar diungkapkan dengan benar.
Konrad Rudolph
1
The Decimaljenis memiliki 93-bit presisi dalam mantissa, dibandingkan dengan sekitar 52 untuk double. Saya berharap Microsoft mendukung format IEEE 80-bit, walaupun itu harus diisi hingga 16 byte; itu akan memungkinkan jangkauan yang lebih besar daripada doubleatau Decimal, kecepatan yang jauh lebih baik daripada Decimal, dukungan untuk operasi transendental (misalnya sin (x), log (x), dll), dan presisi yang sementara tidak sebagus Decimalakan jauh lebih baik daripada double.
supercat
@charlotte: Jika Anda membaca posting lengkap saya, Anda akan melihat bahwa itu dijelaskan.
Noldorin
59

Anda tampaknya cocok dengan manfaat menggunakan tipe floating point. Saya cenderung mendesain desimal dalam semua kasus, dan mengandalkan profiler untuk memberi tahu saya jika operasi pada desimal menyebabkan kemacetan atau perlambatan. Dalam kasus-kasus itu, saya akan "menurunkan casting" menjadi dua kali lipat atau mengambang, tetapi hanya melakukannya secara internal, dan dengan hati-hati mencoba mengelola kehilangan presisi dengan membatasi jumlah digit signifikan dalam operasi matematika yang sedang dilakukan.

Secara umum, jika nilai Anda bersifat sementara (tidak digunakan kembali), Anda aman untuk menggunakan tipe floating point. Masalah sebenarnya dengan tipe floating point adalah tiga skenario berikut.

  1. Anda mengumpulkan nilai floating point (dalam hal ini senyawa kesalahan presisi)
  2. Anda membangun nilai berdasarkan nilai floating point (misalnya dalam algoritma rekursif)
  3. Anda melakukan matematika dengan jumlah digit signifikan yang sangat luas (misalnya, 123456789.1 * .000000000000000987654321)

EDIT

Menurut dokumentasi referensi pada desimal C # :

Kata kunci desimal menunjukkan tipe data 128-bit. Dibandingkan dengan tipe floating-point, tipe desimal memiliki presisi yang lebih besar dan rentang yang lebih kecil, yang membuatnya cocok untuk perhitungan keuangan dan moneter.

Jadi untuk memperjelas pernyataan saya di atas:

Saya cenderung mendesain desimal dalam semua kasus, dan mengandalkan profiler untuk memberi tahu saya jika operasi pada desimal menyebabkan kemacetan atau perlambatan.

Saya hanya pernah bekerja di industri di mana desimal menguntungkan. Jika Anda bekerja pada mesin phsyics atau grafis, mungkin jauh lebih bermanfaat untuk mendesain tipe floating point (float atau double).

Desimal tidak akurat tanpa batas (tidak mungkin untuk merepresentasikan ketelitian tak terbatas untuk non-integral dalam tipe data primitif), tetapi desimal jauh lebih tepat daripada ganda:

  • desimal = 28-29 angka signifikan
  • dobel = 15-16 digit signifikan
  • float = 7 digit signifikan

EDIT 2

Menanggapi komentar Konrad Rudolph , item # 1 (di atas) sudah pasti benar. Agregasi ketidaktepatan memang bertambah. Lihat kode di bawah ini untuk contoh:

private const float THREE_FIFTHS = 3f / 5f;
private const int ONE_MILLION = 1000000;

public static void Main(string[] args)
{
    Console.WriteLine("Three Fifths: {0}", THREE_FIFTHS.ToString("F10"));
    float asSingle = 0f;
    double asDouble = 0d;
    decimal asDecimal = 0M;

    for (int i = 0; i < ONE_MILLION; i++)
    {
        asSingle += THREE_FIFTHS;
        asDouble += THREE_FIFTHS;
        asDecimal += (decimal) THREE_FIFTHS;
    }
    Console.WriteLine("Six Hundred Thousand: {0:F10}", THREE_FIFTHS * ONE_MILLION);
    Console.WriteLine("Single: {0}", asSingle.ToString("F10"));
    Console.WriteLine("Double: {0}", asDouble.ToString("F10"));
    Console.WriteLine("Decimal: {0}", asDecimal.ToString("F10"));
    Console.ReadLine();
}

Ini menghasilkan sebagai berikut:

Three Fifths: 0.6000000000
Six Hundred Thousand: 600000.0000000000
Single: 599093.4000000000
Double: 599999.9999886850
Decimal: 600000.0000000000

Seperti yang Anda lihat, meskipun kami menambahkan dari konstanta sumber yang sama, hasil dari gandanya kurang tepat (meskipun mungkin akan membulat dengan benar), dan float jauh lebih tidak tepat, ke titik di mana ia telah direduksi menjadi hanya dua digit signifikan.

Michael Meadows
sumber
1
Poin 1 salah. Kesalahan presisi / pembulatan hanya terjadi dalam casting, bukan dalam perhitungan. Ini adalah tentu saja benar bahwa sebagian besar operasi matematika tidak stabil, sehingga mengalikan kesalahan. Tetapi ini adalah masalah lain dan itu berlaku sama untuk semua tipe data dengan presisi terbatas, jadi khususnya untuk desimal.
Konrad Rudolph
1
@Konrad Rudolph, lihat contoh di "EDIT 2" sebagai bukti dari poin yang saya coba buat pada item # 1. Seringkali, masalah ini tidak muncul dengan sendirinya karena ketidaktepatan positif menyeimbangkan dengan ketidaktepatan negatif, dan mereka mencuci agregat, tetapi menggabungkan nomor yang sama (seperti yang saya lakukan dalam contoh) menyoroti masalah.
Michael Meadows
Contoh yang bagus Baru ditunjukkan kepada pengembang junior saya, anak-anak kagum.
Machado
Sekarang dapatkah Anda melakukan hal yang sama dengan 2 / 3rds bukannya 3 / 5s ... Anda harus belajar tentang sistem angka sexagesimal yang menangani 2 / 3rds dengan sangat baik.
gnasher729
1
@ gnasher729, menggunakan 2 / 3rds bukannya 3 / 5s tidak ditangani dengan baik untuk jenis yang berbeda. Menariknya, nilai float menghasilkan Single: 667660.400000000000sedangkan nilai desimal dihasilkan Decimal: 666666.7000000000. Nilai float sedikit kurang dari seribu di atas nilai yang benar.
jhenninger
25

Gunakan desimal untuk nilai basis 10, mis. Perhitungan keuangan, seperti yang disarankan orang lain.

Tapi ganda umumnya lebih akurat untuk nilai-nilai yang dihitung sewenang-wenang.

Misalnya, jika Anda ingin menghitung bobot setiap baris dalam portofolio, gunakan gandakan karena hasilnya akan hampir mencapai 100%.

Dalam contoh berikut, doubleResult lebih dekat ke 1 daripada desimalResult:

// Add one third + one third + one third with decimal
decimal decimalValue = 1M / 3M;
decimal decimalResult = decimalValue + decimalValue + decimalValue;
// Add one third + one third + one third with double
double doubleValue = 1D / 3D;
double doubleResult = doubleValue + doubleValue + doubleValue;

Jadi sekali lagi mengambil contoh portofolio:

  • Nilai pasar dari setiap baris dalam portofolio adalah nilai moneter dan mungkin akan digambarkan sebagai desimal.

  • Bobot masing-masing garis dalam portofolio (= Nilai Pasar / SUM (Nilai Pasar)) biasanya lebih baik dinyatakan sebagai dua kali lipat.

Joe
sumber
6

Gunakan dobel atau pelampung ketika Anda tidak membutuhkan ketepatan, misalnya, dalam permainan platformer yang saya tulis, saya menggunakan pelampung untuk menyimpan kecepatan pemain. Jelas saya tidak perlu ketepatan super di sini karena saya akhirnya membulatkan ke Int untuk menggambar di layar.

FlySwat
sumber
3
Ketepatan menjadi satu-satunya keunggulan desimal, ini benar. Anda seharusnya tidak bertanya kapan Anda harus menggunakan angka floating point di atas desimal. Itu harus menjadi pikiran pertama Anda. Pertanyaannya kemudian adalah kapan Anda harus menggunakan desimal (dan jawabannya ada di sini ... ketika ketepatan penting).
Instance Hunter
3
@Daniel Lurus, Ini lucu, tapi saya memiliki pendapat yang berlawanan. Saya pikir menggunakan tipe yang kurang tepat karena karakteristik kinerjanya sama dengan preoptimisasi. Anda berpotensi harus membayar untuk pra-optimasi berkali-kali sebelum Anda menyadari manfaatnya.
Michael Meadows
3
@Michael Meadows, saya bisa mengerti argumen ini. Namun yang perlu dicatat adalah bahwa salah satu keluhan utama dengan optimasi prematur adalah bahwa programmer tidak cenderung tahu apa yang akan lambat. Kita tahu tanpa keraguan, bahwa desimal lebih lambat dari dua kali lipat. Namun demikian, saya kira dalam banyak kasus, peningkatan kinerja tidak akan terlihat oleh pengguna. Tentu saja, dalam banyak kasus, ketepatan juga tidak diperlukan. Heh.
Instance Hunter
Floating-point desimal sebenarnya kurang akurat daripada binary floating-point menggunakan jumlah bit yang sama. Keuntungan desimal adalah dapat secara tepat mewakili pecahan DECIMAL seperti 0,01 yang umum dalam perhitungan keuangan.
dan04
Nah, ini tidak sepenuhnya benar :) - dalam banyak permainan angka floating-point bisa tidak diinginkan, karena fakta bahwa mereka tidak konsisten. Lihat di sini
BlueRaja - Danny Pflughoeft
4

Dalam beberapa Akuntansi, pertimbangkan kemungkinan menggunakan tipe integral sebagai gantinya atau bersamaan. Misalnya, katakan bahwa aturan yang Anda operasikan mengharuskan setiap hasil perhitungan diteruskan dengan setidaknya 6 desimal dan hasil akhir akan dibulatkan ke sen terdekat.

Perhitungan 1/6 dari $ 100 menghasilkan $ 16.66666666666666 ..., sehingga nilai yang dilakukan dalam lembar kerja adalah $ 16.666667. Baik ganda dan desimal harus menghasilkan hasil yang akurat ke 6 tempat desimal. Namun, kita dapat menghindari kesalahan kumulatif dengan membawa hasil ke depan sebagai bilangan bulat 16666667. Setiap perhitungan selanjutnya dapat dilakukan dengan presisi yang sama dan diteruskan dengan cara yang sama. Melanjutkan contoh, saya menghitung pajak penjualan Texas pada jumlah itu (16666667 * .0825 = 1375000). Menambahkan dua (itu adalah lembar kerja pendek) 1666667 + 1375000 = 18041667. Memindahkan titik desimal kembali memberi kita 18,041667, atau $ 18,04.

Meskipun contoh singkat ini tidak akan menghasilkan kesalahan kumulatif menggunakan dobel atau desimal, cukup mudah untuk menunjukkan kasus di mana hanya menghitung dobel atau desimal dan meneruskan akan menumpuk kesalahan yang signifikan. Jika aturan yang Anda operasikan memerlukan sejumlah tempat desimal, simpan setiap nilai sebagai bilangan bulat dengan mengalikan 10 ^ (diperlukan # tempat desimal), dan kemudian bagi dengan 10 ^ (diperlukan # tempat desimal) untuk mendapatkan yang sebenarnya nilai akan menghindari kesalahan kumulatif.

Dalam situasi di mana pecahan uang tidak terjadi (misalnya, mesin penjual otomatis), tidak ada alasan untuk menggunakan jenis yang tidak terpisahkan sama sekali. Anggap saja sebagai menghitung uang, bukan dolar. Saya telah melihat kode di mana setiap perhitungan hanya melibatkan seluruh uang, namun penggunaan ganda menyebabkan kesalahan! Matematika integer saja menghapus masalah. Jadi, jawaban saya yang tidak konvensional adalah, jika mungkin, lupakan dobel dan desimal.

G DeMasters
sumber
3

Jika Anda perlu melakukan binary interrop dengan bahasa atau platform lain, maka Anda mungkin perlu menggunakan float atau double, yang terstandarisasi.

Will Dean
sumber
2

Catatan: posting ini didasarkan pada informasi kemampuan tipe desimal dari http://csharpindepth.com/Articles/General/Decimal.aspx dan interpretasi saya sendiri tentang apa artinya itu. Saya akan menganggap ganda adalah presisi ganda normal IEEE.

Note2: terkecil dan terbesar di pos ini merujuk pada besarnya angka.

Pro "desimal".

  • "desimal" dapat mewakili angka-angka persis yang dapat ditulis sebagai fraksi desimal (cukup pendek), dobel tidak bisa. Ini penting dalam buku besar keuangan dan serupa di mana penting bahwa hasilnya persis sama dengan apa yang manusia lakukan dalam perhitungan.
  • "desimal" memiliki mantissa yang jauh lebih besar daripada "ganda". Itu berarti bahwa untuk nilai-nilai di dalam rentang normal itu "desimal" akan memiliki presisi yang jauh lebih tinggi daripada ganda.

Kontra desimal

  • Ini akan menjadi jauh lebih lambat (saya tidak memiliki tolok ukur tapi saya akan menebak setidaknya urutan besarnya mungkin lebih), desimal tidak akan mendapat manfaat dari akselerasi perangkat keras dan aritmatika di atasnya akan memerlukan penggandaan / pembagian yang relatif mahal dengan kekuatan 10 ( yang jauh lebih mahal daripada perkalian dan pembagian dengan kekuatan 2) untuk mencocokkan eksponen sebelum penambahan / pengurangan dan untuk membawa eksponen kembali ke dalam jangkauan setelah penggandaan / pembagian.
  • desimal akan meluap lebih awal dari dua kali lipat. desimal hanya dapat mewakili angka hingga ± 2 96 -1. Dengan perbandingan ganda dapat mewakili angka hingga hampir ± 2 1024
  • desimal akan lebih rendah dari awal. Angka terkecil yang diwakili dalam desimal adalah ± 10 -28 . Dengan perbandingan, ganda dapat mewakili nilai ke 2 -149 (sekitar 10 -45 ) jika nomor subnromal didukung dan 2 -126 (sekitar 10 -38 ) jika tidak.
  • desimal membutuhkan memori dua kali lebih banyak dari dua kali lipat.

Pendapat saya adalah bahwa Anda harus menggunakan "desimal" untuk pekerjaan uang dan kasus lain di mana pencocokan perhitungan manusia sangat penting dan Anda harus menggunakan penggunaan ganda sebagai pilihan default Anda sepanjang waktu.

plugwash
sumber
2

Tergantung pada apa yang Anda butuhkan.

Karena float dan double tipe data biner Anda memiliki beberapa diifculties dan errrors di dalam angka bulat, jadi misalnya double akan bulat 0,1 hingga 0,100000001490116, ganda juga akan bulat 1/3 hingga 0,33333334326441. Sederhananya tidak semua bilangan real memiliki representasi akurat dalam tipe ganda

Untungnya C # juga mendukung apa yang disebut aritmatika titik-mengambang desimal, di mana angka-angka diwakili melalui sistem numerik desimal daripada sistem biner. Dengan demikian, aritmetika floating point desimal tidak kehilangan keakuratan saat menyimpan dan memproses angka floating-point. Ini membuatnya sangat cocok untuk perhitungan di mana tingkat akurasi yang tinggi diperlukan.

Neil Meyer
sumber
0

Gunakan floating point jika Anda menghargai kinerja melebihi kebenaran.

Mark Brackett
sumber
6
Angka desimal tidak lebih benar, kecuali dalam kasus terbatas tertentu yang kadang-kadang (tidak berarti selalu) penting.
David Thornley
0

Pilih jenis fungsi aplikasi Anda. Jika Anda membutuhkan ketelitian seperti dalam analisis keuangan, Anda telah menjawab pertanyaan Anda. Tetapi jika aplikasi Anda dapat menyelesaikan dengan estimasi Anda ok dengan ganda.

Apakah aplikasi Anda membutuhkan perhitungan cepat atau akankah ia punya waktu di dunia untuk memberi Anda jawaban? Itu benar-benar tergantung pada jenis aplikasi.

Grafis lapar? float atau double sudah cukup. Analisis data keuangan, meteor yang menyerang planet semacam presisi? Mereka akan membutuhkan sedikit presisi :)

Khan
sumber
8
Angka desimal juga merupakan perkiraan. Mereka sesuai dengan konvensi aritmatika keuangan, tetapi tidak ada keuntungan dalam, katakanlah, perhitungan yang melibatkan fisika.
David Thornley
0

Desimal memiliki byte yang lebih luas, dobel didukung oleh CPU. Desimal adalah basis-10, jadi konversi desimal ke dua terjadi saat desimal dihitung.

For accounting - decimal
For finance - double
For heavy computation - double

Perlu diingat. NET CLR hanya mendukung Math.Pow (double, double). Desimal tidak didukung.

.NET Framework 4

[SecuritySafeCritical]
public static extern double Pow(double x, double y);
Jeson Martajaya
sumber
0

Nilai ganda akan bersambung ke notasi ilmiah secara default jika notasi itu lebih pendek dari tampilan desimal. (mis. 0,00000003 akan menjadi 3e-8) Nilai desimal tidak akan pernah bersambung ke notasi ilmiah. Ketika membuat serial untuk dikonsumsi oleh pihak eksternal, ini mungkin menjadi pertimbangan.

chris klassen
sumber