Mengapa .NET menggunakan pembulatan bankir sebagai default?

271

Menurut dokumentasi, decimal.Roundmetode ini menggunakan algoritma round-to-even yang tidak umum untuk sebagian besar aplikasi. Jadi saya selalu berakhir menulis fungsi kustom untuk melakukan algoritma setengah-setengah yang lebih alami:

public static decimal RoundHalfUp(this decimal d, int decimals)
{
    if (decimals < 0)
    {
        throw new ArgumentException("The decimals must be non-negative", 
            "decimals");
    }

    decimal multiplier = (decimal)Math.Pow(10, decimals);
    decimal number = d * multiplier;

    if (decimal.Truncate(number) < number)
    {
        number += 0.5m;
    }
    return decimal.Round(number) / multiplier;
}

Apakah ada yang tahu alasan di balik keputusan desain kerangka kerja ini?

Apakah ada implementasi built-in dari algoritma round-half-up ke dalam kerangka kerja? Atau mungkin beberapa API Windows yang tidak dikelola?

Ini bisa menyesatkan bagi pemula yang hanya menulis decimal.Round(2.5m, 0)mengharapkan 3 sebagai hasilnya tetapi mendapatkan 2 sebagai gantinya.

Darin Dimitrov
sumber
105
Membulatkan bukanlah "lebih alami." Alam tidak ada hubungannya dengan itu. Itu hanya apa yang Anda pelajari di sekolah dasar ketika Anda mempelajari konsep "pembulatan." Pelajaran-pelajaran di sekolah tidak selalu menggambarkan gambaran lengkap.
Rob Kennedy
45
@Rob Dan itu sebabnya itu lebih alami , meskipun itu tidak benar
Pacerier
10
Saya tidak mengerti, @Pacerier. Saya menjelaskan mengapa hal itu tidak alami, dan Anda mengatakan itu sebenarnya mengapa adalah alami. Bagaimana argumen saya bertentangan dengan kesimpulan saya, yang merupakan kebalikan dari argumen Anda? Hal-hal yang Anda sudah terbiasa mungkin terasa alami, dan kadang-kadang kita secara kiasan mengatakan bahwa sesuatu adalah "sifat kedua," tetapi itu tidak membuat mereka alami.
Rob Kennedy
16
@Rob Saya katakan itu alami, karena rasanya alami. Anda tahu bahwa ada 36 objek yang berbeda dengan nama variabel yang sama alami yang tepat?
Pacerier
9
analog alam, jadi itu kata yang salah untuk digunakan; tapi ini terlalu berlebihan. Mungkin 'biasa' akan menjadi kata yang lebih baik untuk digunakan .. "apa pembulatan yang biasa dilakukan orang"> 0,5 menuju 1,0
whytheq

Jawaban:

196

Mungkin karena algoritma yang lebih baik. Selama banyak pembulatan dilakukan, Anda akan rata-rata bahwa semua 0,5 akhirnya sama-sama naik dan turun. Ini memberikan estimasi yang lebih baik dari hasil aktual jika Anda misalnya, menambahkan sekelompok angka bulat. Saya akan mengatakan bahwa meskipun bukan itu yang diharapkan beberapa orang, itu mungkin hal yang lebih benar untuk dilakukan.

Kibbee
sumber
71
dengan asumsi Anda memiliki distribusi datar input ganjil dan genap tentunya
jk.
7
+1 untuk algoritme yang lebih baik , meskipun Ostemar memiliki jawaban yang sebenarnya ( stackoverflow.com/questions/311696/… )
Ian Boyd
2
@Ian, saya memberikan jawaban itu +1 juga. Pokoknya kita bisa mendapatkan "jawaban yang diterima dipindahkan" Mungkin OP bisa melakukan ini. Jawaban sebenarnya untuk "mengapa" menggunakan metode ini adalah setengah jalan ke bawah halaman. Meskipun saya sangat menyukai peningkatan perwakilan, saya mendapat sekitar seminggu sekali dari jawaban ini.
Kibbee
@ Kibbee - terima kasih telah mendorong jawaban saya. Saya kira terserah OP untuk mengubah jawaban yang diterima sesuai keinginannya?
Ostemar
-1 untuk menyatakan itu adalah algoritma yang lebih baik. - Diberikan sampel angka acak menggunakan pembulatan bankir, Anda akan memiliki lebih banyak angka pada posisi genap daripada posisi ganjil. - Hanya setelah Anda rata-rata angka-angka itu Anda mendapatkan lagi spread yang sama dengan distribusi asli. - Namun, jika Anda misalnya akan memplot data ini dalam sebar plot, orang mungkin melihat pengelompokan buatan.
paul23
437

Jawaban yang lain dengan alasan mengapa algoritma Banker (alias setengah putaran ke genap ) adalah pilihan yang baik cukup benar. Itu tidak menderita dari bias negatif atau positif sebanyak setengah putaran dari nol metode atas distribusi yang paling masuk akal.

Tetapi pertanyaannya adalah mengapa .NET menggunakan pembulatan aktual Banker sebagai default - dan jawabannya adalah bahwa Microsoft telah mengikuti standar IEEE 754 . Ini juga disebutkan dalam MSDN untuk Math.Round di bawah Keterangan.

Perhatikan juga bahwa .NET mendukung metode alternatif yang ditentukan oleh IEEE dengan menyediakan MidpointRoundingenumerasi. Mereka tentu saja dapat memberikan lebih banyak alternatif untuk menyelesaikan ikatan, tetapi mereka memilih untuk hanya memenuhi standar IEEE.

Ostemar
sumber
17
Jadi, mengapa IEEE 754 mengikuti pembulatan bankir? Jawaban (masih bagus) ini hanya melewati ember.
Henk Holterman
4
@HenkHolterman Mungkin karena apa yang disebutkan dalam jawaban lain (dan seperti yang saya rangkum); itu tidak menderita (terlalu banyak) dari bias negatif atau positif dan karena itu membuat default lebih beresonansi untuk sebagian besar distribusi dan domain masalah.
Ostemar
Saya pikir ini menarik karena IEEE 754 adalah standar untuk angka floating point yang bukan Desimal. Mungkin mengikuti IEEE 754 sehingga algoritma yang sama digunakan untuk pembulatan Double sebagai Desimal.
Brandon Barkley
1
@BrandonBarkley A Desimal atau desimal adalah angka floating point, dan IEEE 754 memang menyertakan angka floating point desimal .
Pablo H
88

Meskipun saya tidak dapat menjawab pertanyaan "Mengapa desainer Microsoft memilih ini sebagai default?", Saya hanya ingin menunjukkan bahwa fungsi tambahan tidak diperlukan.

Math.Roundmemungkinkan Anda menentukan MidpointRounding:

  • ToEven - Ketika sebuah angka berada di antara dua lainnya, itu dibulatkan ke angka genap terdekat.
  • AwayFromZero - Ketika angka berada di tengah antara dua yang lain, itu dibulatkan ke angka terdekat yang jauh dari nol.
Michael Stum
sumber
8
Dan seperti yang saya sebutkan di utas terkait, pastikan bahwa Anda konsisten dalam pembulatan Anda - jika Anda kadang-kadang melakukan pembulatan dalam database, dan kadang-kadang dalam .net, Anda akan memiliki kesalahan aneh, satu sen yang akan membawa Anda minggu ke mencari tahu.
chris
59
Seorang klien pernah membayar saya lebih dari $ 40.000 untuk melacak kesalahan pembulatan $ 0,11 antara dua angka yang sama-sama kurang dari 1 MILIAR dolar; $ 0,11 adalah karena perbedaan kesalahan pembulatan digit ke-8 antara mainframe dan SQL Server. Bicara tentang perfeksionis!
EJ Brennan
12
@ EJB - saya mungkin akan menjadi perfeksionis jika saya berurusan dengan satu miliar dolar ;-)
royse41
12
@ EJ Brennan: Butuh $ 40k untuk mencari tahu? Saya melihat masalah seperti ini setiap saat, pembulatan adalah penyebab # 1, normalisasi ganda / float adalah penyebab # 2, kesalahan pemrogram # 3 - # 3 dapat langsung diatur ke # 1 jika tidak ada kasus uji yang telah ditentukan sebelumnya. btw dapat tolong hubungi saya dengan klien miliar saya, saya pikir saya bisa menemukan beberapa bug $ 40k lebih dalam sistemnya juga! : D
3
@seanxe: Atau jika Anda pernah melihat Ruang Kantor. Serius, setiap kali Anda melihat ketidakakuratan uang yang misterius dan kecil, memecahkan misteri bagaimana persisnya mereka terjadi hampir selalu merupakan ide yang bagus. Mungkin Anda akan memutuskan untuk tidak memperbaiki bug, tetapi mengetahui penyebab dasarnya masih memiliki nilai. Saya yakin banyak orang yang bekerja dengan uang senang melihat ketidakakuratan yang sangat kecil.
Brian
22

Desimal sebagian besar digunakan untuk uang ; pembulatan bankir adalah hal biasa ketika bekerja dengan uang . Atau bisa dibilang.

Sebagian besar bankir yang membutuhkan tipe desimal; karena itu ia melakukan "pembulatan bankir"

Pembulatan bankir memiliki keuntungan bahwa rata-rata Anda akan mendapatkan hasil yang sama jika Anda:

  • bulat satu set "garis faktur" sebelum menambahkannya,
  • atau tambahkan mereka lalu bulatkan totalnya

Pembulatan sebelum dijumlahkan menghemat banyak pekerjaan di hari-hari sebelum komputer.

(Di Inggris ketika kami menggunakan bank desimal tidak akan berurusan dengan setengah pence, tetapi selama bertahun-tahun masih ada koin setengah pence dan toko sering memiliki harga yang berakhir dengan setengah pence - begitu banyak pembulatan)

Ian Ringrose
sumber
8
"Desimal sebagian besar digunakan untuk uang" ... dan segala sesuatu yang bukan bilangan bulat.
John Tyree
3
@ JohnTyree, Tidak benar sebagian besar waktu double / float digunakan ketika itu bukan bilangan bulat. lihat stackoverflow.com/questions/2545567/…
Ian Ringrose
3
Wow. Kesalahan konyol di pihak saya. Decmialya. Desimal, tidak. Untuk anak cucu, saya setuju dengan sentimen asli di sini.
John Tyree
Bankir mungkin suka pembulatan bankir, tetapi pembukuan mungkin bukan penggemar itu, mereka mengatakan bahwa perbedaan 0,005 harus bulat menyebabkan dibulatkan dengan 0,01, tidak tergantung apakah itu angka ganjil atau genap.
JustAMartin
0

Gunakan kelebihan fungsi Round lainnya seperti ini:

decimal.Round(2.5m, 0,MidpointRounding.AwayFromZero)

Ini akan menghasilkan 3 . Dan jika Anda gunakan

decimal.Round(2.5m, 0,MidpointRounding.ToEven)

Anda akan mendapatkan pembulatan bankir.

Omid Sadeghi
sumber
4
Ini tidak menjawab pertanyaan mengapa Rounder Banker dipilih sebagai default.
shortstuffsushi