เหตุใด. NET จึงใช้การปัดเศษของนายธนาคารเป็นค่าเริ่มต้น


271

ตามเอกสารอธิบายdecimal.Roundวิธีการใช้อัลกอริธึมแบบรอบต่อคู่ซึ่งไม่ใช่เรื่องธรรมดาสำหรับแอปพลิเคชันส่วนใหญ่ ดังนั้นฉันมักจะเขียนฟังก์ชันแบบกำหนดเองเพื่อทำอัลกอริธึมแบบครึ่งรอบที่เป็นธรรมชาติมากขึ้น:

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;
}

ไม่มีใครรู้เหตุผลที่อยู่เบื้องหลังการตัดสินใจออกแบบกรอบงานนี้

มีการใช้งานอัลกอริธึมแบบครึ่งรอบในตัวหรือไม่? หรืออาจมี Windows API ที่ไม่มีการจัดการบ้าง

มันอาจทำให้เข้าใจผิดสำหรับผู้เริ่มต้นที่เพียงเขียนdecimal.Round(2.5m, 0)คาดหวัง 3 เป็นผล แต่รับ 2 แทน


105
การปัดเศษขึ้นไม่ใช่ "เป็นธรรมชาติมากขึ้น" ธรรมชาติไม่มีส่วนเกี่ยวข้องกับมัน มันเป็นเพียงสิ่งที่คุณเรียนรู้ในเกรดเมื่อคุณเรียนรู้แนวคิดของ "การปัดเศษ" บทเรียน Gradeschool ไม่ได้วาดภาพเต็มเสมอไป
Rob Kennedy

45
@Rob และนั่นคือเหตุผลที่มันเป็นธรรมชาติมากขึ้นแม้ว่ามันจะไม่ถูกต้อง
Pacerier

10
ฉันไม่เข้าใจ @Pacerier ฉันอธิบายว่าทำไมมันไม่เป็นธรรมชาติและคุณบอกว่าที่จริงแล้วมันเป็นเรื่องธรรมดา ข้อโต้แย้งของฉันทำงานอย่างไรกับข้อสรุปของฉันซึ่งเป็นสิ่งที่ตรงกันข้ามกับคุณ? สิ่งที่คุณคุ้นเคยอาจรู้สึกเป็นธรรมชาติและบางครั้งเราเปรียบเปรยว่าบางสิ่งเป็น "ธรรมชาติรอง" แต่นั่นไม่ได้ทำให้เป็นธรรมชาติ
Rob Kennedy

16
@ Rob ฉันว่ามันเป็นเรื่องธรรมชาติเพราะมันให้ความรู้สึกเป็นธรรมชาติ คุณไม่ทราบว่ามี 36 วัตถุที่แตกต่างแบบเดียวกับที่ชื่อตัวแปรธรรมชาติใช่มั้ย?
Pacerier

9
อะนาล็อกที่ผิดธรรมชาติของธรรมชาติดังนั้นจึงเป็นคำที่ผิดที่จะใช้ แต่สิ่งนี้กำลังอวดรู้ บางที 'ปกติ' อาจเป็นคำที่ดีกว่าในการใช้ .. "การปัดเศษตามปกติที่ผู้คนทำ" คือ 0.5 ไปที่ 1.0
ทำไม

คำตอบ:


196

อาจเป็นเพราะมันเป็นอัลกอริทึมที่ดีกว่า ในระหว่างรอบการแข่งหลายรอบคุณจะเฉลี่ยว่าการจบรอบ. 5 ทั้งหมดนั้นการขึ้นและลงอย่างเท่าเทียมกัน สิ่งนี้ให้การประมาณที่ดีกว่าของผลลัพธ์ที่เกิดขึ้นจริงหากคุณเป็นเช่นการเพิ่มจำนวนของตัวเลขที่ปัดเศษ ฉันจะบอกว่าแม้ว่ามันจะไม่ใช่สิ่งที่บางคนคาดหวัง แต่มันก็เป็นสิ่งที่ถูกต้องมากกว่า


71
สมมติว่าคุณมีการแจกแจงแบบคงที่ของข้อมูลคี่และคู่อย่างแน่นอน
jk

7
+1 สำหรับขั้นตอนวิธีการที่ดีกว่าแม้ว่า Ostemar มีจริงคำตอบ ( stackoverflow.com/questions/311696/... )
เอียนบอยด์

2
@ ฉันจะให้คำตอบ +1 ด้วย อย่างไรก็ตามเราสามารถรับ "คำตอบที่ยอมรับแล้วย้ายได้" บางที OP อาจทำเช่นนี้ได้ คำตอบที่แท้จริงของคำว่า "ทำไม" ใช้วิธีนี้คือครึ่งหน้าเว็บ แม้ว่าฉันจะชอบการเพิ่มตัวแทนฉันได้รับประมาณสัปดาห์ละครั้งจากคำตอบนี้
Kibbee

@Kibbee - ขอบคุณที่ผลักดันคำตอบของฉัน ฉันเดาว่ามันขึ้นอยู่กับ OP เพื่อเปลี่ยนคำตอบที่ยอมรับในขณะที่เขาเห็นว่าเหมาะสม?
Ostemar

-1 สำหรับการระบุว่าเป็นอัลกอริทึมที่ดีกว่า - จากการสุ่มตัวเลขโดยใช้การปัดเศษของเจ้ามือคุณจะได้ตัวเลขที่ตำแหน่งสม่ำเสมอมากกว่าตำแหน่งคี่ - เป็นเพียงหลังจากที่คุณเฉลี่ยตัวเลขเหล่านั้นว่าคุณได้รับสเปรดที่คล้ายกันกับการกระจายตัวเดิมอีกครั้ง - อย่างไรก็ตามหากคุณต้องการพล็อตข้อมูลนี้ในพล็อตกระจายคุณอาจเห็นการจัดกลุ่มแบบเทียม
paul23

437

คำตอบอื่น ๆ ด้วยเหตุผลว่าทำไมอัลกอริธึมของนายธนาคาร (หรือที่รู้จักกันตั้งแต่ครึ่งจนถึงถึง ) เป็นตัวเลือกที่ดีค่อนข้างถูกต้อง มันไม่ได้รับความลำเอียงเชิงลบหรือบวกเท่าครึ่งรอบห่างจากวิธีการเป็นศูนย์ในการแจกแจงที่สมเหตุสมผลที่สุด

แต่คำถามคือเหตุผลว่าทำไม .NET ใช้ปัดเศษที่เกิดขึ้นจริงของธนาคารเป็นค่าเริ่มต้น - และคำตอบคือว่าไมโครซอฟท์ได้ดำเนินการตามมาตรฐาน IEEE 754มาตรฐาน สิ่งนี้ถูกกล่าวถึงในMSDN สำหรับ Math.Roundภายใต้ข้อสังเกต

นอกจากนี้โปรดทราบว่า. NET รองรับวิธีการอื่นที่ระบุโดย IEEE โดยจัดให้มีการMidpointRoundingแจงนับ แน่นอนว่าพวกเขาสามารถให้ทางเลือกมากขึ้นในการแก้ปัญหาความสัมพันธ์ แต่พวกเขาเลือกที่จะทำตามมาตรฐาน IEEE


17
ดังนั้นทำไม IEEE 754 จึงติดตามการปัดเศษของธนาคาร คำตอบนี้ (ยังดี) เพียงแค่ผ่านที่ฝากข้อมูล
Henk Holterman

4
@ HenkHolterman อาจเป็นเพราะสิ่งที่กล่าวถึงในคำตอบอื่น ๆ (และตามที่ฉันสรุป); มันไม่ได้รับผลกระทบ (มากเกินไป) จากอคติเชิงลบหรือบวกและทำให้ค่าเริ่มต้นที่เป็นกันเองมากขึ้นสำหรับการแจกแจงและโดเมนปัญหาส่วนใหญ่
Ostemar


ฉันคิดว่ามันน่าสนใจเพราะ IEEE 754 เป็นมาตรฐานสำหรับตัวเลขทศนิยมซึ่งไม่ใช่ทศนิยม อาจเป็นไปตาม IEEE 754 เพื่อให้อัลกอริทึมเดียวกันสำหรับการปัดเศษเป็นทศนิยม
Brandon Barkley

1
@BrandonBarkley ทศนิยมหรือทศนิยม เป็นตัวเลขทศนิยมและIEEE 754ไม่รวมถึงตัวเลขทศนิยมทศนิยม
Pablo H

88

ในขณะที่ฉันไม่สามารถตอบคำถามของ "ทำไมนักออกแบบของ Microsoft เลือกสิ่งนี้เป็นค่าเริ่มต้น" ฉันแค่ต้องการชี้ให้เห็นว่าฟังก์ชั่นพิเศษไม่จำเป็น

Math.Roundช่วยให้คุณระบุMidpointRounding:

  • ToEven - เมื่อตัวเลขอยู่กึ่งกลางระหว่างสองตัวมันจะถูกปัดเศษเป็นเลขคู่ที่ใกล้ที่สุด
  • AwayFromZero - เมื่อตัวเลขอยู่ครึ่งทางระหว่างสองหมายเลขนั้นจะถูกปัดเศษเป็นตัวเลขที่ใกล้ที่สุดซึ่งอยู่ห่างจากศูนย์

8
และอย่างที่ฉันได้กล่าวไว้ในหัวข้อที่เกี่ยวข้องตรวจสอบให้แน่ใจว่าคุณมีความสอดคล้องในการปัดเศษ - บางครั้งคุณปัดเศษในฐานข้อมูลและบางครั้งใน. net คุณจะมีข้อผิดพลาดแปลก ๆ ร้อยเปอร์เซ็นต์ซึ่งจะทำให้คุณ คิดออก
chris

59
ครั้งหนึ่งลูกค้าจ่ายเงินให้ฉันมากกว่า $ 40,000 เพื่อติดตามข้อผิดพลาดในการปัดเศษ $ 0.11 ระหว่างตัวเลขสองตัวที่ทั้งสองขี้อายเพียง 1 พันล้านดอลลาร์ $ 0.11 นั้นเกิดจากความแตกต่างในข้อผิดพลาดในการปัดเศษตัวเลขที่ 8 ระหว่างเมนเฟรมและ SQL Server พูดคุยเกี่ยวกับสิ่งดีเลิศ!
EJ Brennan

12
@EJB - ฉันอาจจะเป็นพวกชอบความสมบูรณ์แบบถ้าฉันจัดการกับหนึ่งพันล้านดอลลาร์ ;-)
royse41

12
@EJ Brennan: คุณใช้เงิน $ 40k ในการคิดมันออกมา? ฉันเห็นปัญหาเช่นนี้ตลอดเวลาการปัดเศษเป็นสาเหตุ # 1, double / float normalization เป็นสาเหตุ # 2, โปรแกรมเมอร์ข้อผิดพลาด # 3 - # 3 สามารถตั้งค่าทันทีเป็น # 1 หากไม่มีกรณีทดสอบที่กำหนดไว้ล่วงหน้า btw คุณช่วยวางฉันในการติดต่อกับลูกค้ามหาเศรษฐีของคุณฉันคิดว่าฉันสามารถหาข้อบกพร่องอีก $ 40k เพิ่มเติมในระบบของเขาด้วย! : D

3
@seanxe: หรือถ้าคุณเห็น Office Space อย่างจริงจังเมื่อใดก็ตามที่คุณเห็นเงินไม่ถูกต้องลึกลับเล็ก ๆ ในการแก้ปัญหาความลึกลับของสิ่งที่เกิดขึ้นพวกเขาเกือบจะเป็นความคิดที่ดี เป็นไปได้ที่คุณจะตัดสินใจไม่แก้ไขข้อบกพร่อง แต่รู้สาเหตุที่เป็นต้นเหตุยังคงมีคุณค่า ฉันเดิมพันหลายคนที่ทำงานกับเงินมีความสุขที่จะสังเกตเห็นความไม่ถูกต้องแม้แต่น้อย
Brian

22

ทศนิยมส่วนใหญ่จะใช้สำหรับเงิน ; การปัดเศษของนายธนาคารเป็นเรื่องธรรมดาเมื่อทำงานกับเงิน หรือคุณสามารถพูดได้

มันเป็นนายธนาคารส่วนใหญ่ที่ต้องการประเภททศนิยม ดังนั้นมันจึงเป็น "การปัดเศษของนายธนาคาร"

การปัดเศษของธนาคารมีความได้เปรียบโดยเฉลี่ยคุณจะได้รับผลลัพธ์ที่เหมือนกันหากคุณ:

  • ปัดเศษชุดของ "บรรทัดใบแจ้งหนี้" ก่อนที่จะเพิ่ม
  • หรือเพิ่มขึ้นแล้วรวมผลรวม

การปัดเศษก่อนที่จะเพิ่มการบันทึกช่วยให้ทำงานได้มากในหลายวันก่อนคอมพิวเตอร์

(ในสหราชอาณาจักรเมื่อเราไปธนาคารทศนิยมจะไม่จัดการกับเพนนีครึ่ง แต่เป็นเวลาหลายปียังมีเหรียญเพนนีครึ่งและร้านค้ามักจะมีราคาสิ้นสุดในครึ่งเพนนี - การปัดเศษจำนวนมาก)


8
"ทศนิยมส่วนใหญ่ใช้เพื่อเงิน" ... และทุกอย่างอื่นที่ไม่ใช่จำนวนเต็ม
John Tyree

3
@JohnTyree, ไม่เป็นความจริงส่วนใหญ่จะใช้ double / float เมื่อไม่ใช่จำนวนเต็ม ดูstackoverflow.com/questions/2545567/…
Ian Ringrose

3
ว้าว. ความผิดพลาดที่ไร้สาระในส่วนของฉัน Decmialใช่ ทศนิยมไม่. สำหรับลูกหลานฉันเห็นด้วยกับความรู้สึกดั้งเดิมที่นี่
John Tyree

นายธนาคารอาจชอบการปัดเศษของนายธนาคาร แต่ผู้ทำบัญชีอาจไม่ใช่แฟนของพวกเขาพวกเขากล่าวว่าความแตกต่าง 0.005 ควรนำไปสู่การปัดเศษขึ้น 0.01 ไม่ขึ้นอยู่กับว่าเป็นเลขคี่หรือเลขคู่
JustAMartin

0

ใช้ฟังก์ชั่น Overload อีกอันของ Round ดังนี้:

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

มันจะออก3 และถ้าคุณใช้

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

คุณจะได้รับการปัดเศษของนายธนาคาร


4
สิ่งนี้ไม่ตอบคำถามว่าทำไมการปัดเศษของธนาคารจึงถูกเลือกเป็นค่าเริ่มต้น
shortstuffsushi
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.