Tại sao .NET sử dụng làm tròn ngân hàng làm mặc định?


271

Theo tài liệu này, decimal.Roundphương pháp này sử dụng thuật toán làm tròn, thậm chí không phổ biến đối với hầu hết các ứng dụng. Vì vậy, tôi luôn luôn kết thúc việc viết một hàm tùy chỉnh để thực hiện thuật toán làm tròn nửa tự nhiên hơn:

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

Có ai biết lý do đằng sau quyết định thiết kế khung này không?

Có bất kỳ triển khai tích hợp nào của thuật toán nửa vòng vào khung không? Hoặc có thể một số API Windows không được quản lý?

Nó có thể gây hiểu lầm cho người mới bắt đầu chỉ đơn giản là viết decimal.Round(2.5m, 0)mong đợi 3 là kết quả nhưng thay vào đó lại nhận được 2.


105
Làm tròn không phải là "tự nhiên hơn." Thiên nhiên không có gì để làm với nó. Nó chỉ đơn giản là những gì bạn đã học ở trường mẫu giáo khi bạn học khái niệm "làm tròn". Bài học ở trường đại học không phải lúc nào cũng vẽ một bức tranh đầy đủ.
Rob Kennedy

45
@Rob Và đó là lý do tại sao nó tự nhiên hơn , mặc dù điều đó không đúng
Pacerier

10
Tôi không hiểu, @Pacerier. Tôi đã giải thích lý do tại sao nó không tự nhiên và bạn nói rằng thực tế nó tự nhiên. Làm thế nào để lập luận của tôi làm việc chống lại kết luận của tôi, điều ngược lại với bạn? Những điều bạn đã quen với việc có thể cảm thấy tự nhiên, và đôi khi chúng tôi nói theo nghĩa bóng rằng một cái gì đó là "bản chất thứ hai", nhưng điều đó không làm cho chúng tự nhiên.
Rob Kennedy

16
@Rob Tôi đang nói nó là tự nhiên, bởi vì nó cảm thấy tự nhiên. Bạn có biết rằng có 36 đối tượng khác nhau có cùng tên biến tự nhiên phải không?
Pacerier

9
tự nhiên là tương tự chắc chắn vì vậy đó là từ sai để sử dụng; nhưng điều này đang được mô phạm Có lẽ 'thông thường' sẽ là một từ tốt hơn để sử dụng .. "cách làm tròn thông thường mà mọi người thường làm"> 0,5 chuyển sang 1.0
whytheq

Câu trả lời:


196

Có lẽ bởi vì đó là một thuật toán tốt hơn. Trong quá trình thực hiện nhiều vòng, bạn sẽ tính trung bình rằng tất cả các kết thúc của 0,5 đều lên và xuống bằng nhau. Điều này cho ước tính tốt hơn về kết quả thực tế nếu bạn là ví dụ, thêm một loạt các số làm tròn. Tôi sẽ nói rằng mặc dù đó không phải là điều mà một số người có thể mong đợi, nhưng có lẽ đó là điều đúng đắn hơn để làm.


71
giả sử bạn có một phân phối cố định của các đầu vào lẻ ​​và thậm chí đầu vào
jk.

7
+1 cho thuật toán tốt hơn , mặc dù Ostemar có câu trả lời thực tế ( stackoverflow.com/questions/311696/ mẹo )
Ian Boyd

2
@Ian, tôi cũng cho câu trả lời +1 đó. Dù sao, chúng ta có thể nhận được "câu trả lời được chấp nhận di chuyển" Có lẽ OP có thể làm điều này. Câu trả lời thực sự cho "tại sao" nó sử dụng phương pháp này là một nửa trang. Mặc dù tôi khá thích rep boost nhưng tôi nhận được khoảng một lần một tuần từ câu trả lời này.
Kibbee

@Kibbee - cảm ơn vì đã đẩy câu trả lời của tôi lên. Tôi đoán rằng tùy thuộc vào OP để thay đổi câu trả lời được chấp nhận khi anh ấy thấy phù hợp?
Ostemar

-1 để nói rằng đó là một thuật toán tốt hơn. - Đưa ra một mẫu số ngẫu nhiên sử dụng làm tròn số của ngân hàng, bạn sẽ có nhiều số ở các vị trí chẵn hơn các vị trí lẻ. - Chỉ sau khi bạn tính trung bình những con số đó, bạn mới nhận được một mức chênh lệch tương tự với phân phối ban đầu. - Tuy nhiên, nếu bạn ví dụ sẽ vẽ dữ liệu này trong một biểu đồ phân tán, người ta có thể thấy nhóm nhân tạo.
paul23

437

Các câu trả lời khác với lý do tại sao thuật toán của Ngân hàng (còn gọi là nửa tròn đến chẵn ) là một lựa chọn tốt là hoàn toàn chính xác. Nó không bị sai lệch âm hoặc dương nhiều như nửa vòng so với phương pháp 0 so với hầu hết các phân phối hợp lý.

Nhưng câu hỏi đặt ra là tại sao .NET sử dụng làm tròn thực tế của Banker làm mặc định - và câu trả lời là Microsoft đã tuân theo tiêu chuẩn IEEE 754 . Điều này cũng được đề cập trong MSDN cho Math.Round dưới chú thích.

Cũng lưu ý rằng .NET hỗ trợ phương thức thay thế được chỉ định bởi IEEE bằng cách cung cấp MidpointRoundingbảng liệt kê. Tất nhiên họ có thể đã cung cấp nhiều lựa chọn thay thế hơn để giải quyết các mối quan hệ, nhưng họ chọn chỉ hoàn thành tiêu chuẩn IEEE.


17
Vì vậy, tại sao IEEE 754 theo dõi các nhân viên ngân hàng? Câu trả lời này (vẫn tốt) chỉ cần vượt qua xô.
Henk Holterman

4
@HenkHolterman Có lẽ là do những gì được đề cập trong các câu trả lời khác (và như tôi đã tóm tắt); nó không chịu (quá nhiều) từ sai lệch âm hoặc dương và do đó làm cho một mặc định dễ cộng hưởng hơn cho hầu hết các phân phối và miền vấn đề.
Ostemar


Tôi nghĩ điều đó thật thú vị bởi vì IEEE 754 là tiêu chuẩn cho các số dấu phẩy động mà Decimal không có. Có thể nó tuân theo IEEE 754 để thuật toán tương tự được sử dụng để làm tròn Double là Decimal.
Brandon Barkley

1
@BrandonBarkley Số thập phân hoặc số thập phân số dấu phẩy động và IEEE 754 không bao gồm số dấu phẩy động thập phân .
Pablo H

88

Mặc dù tôi không thể trả lời câu hỏi "Tại sao các nhà thiết kế của Microsoft lại chọn điều này làm mặc định?", Tôi chỉ muốn chỉ ra rằng một chức năng bổ sung là không cần thiết.

Math.Roundcho phép bạn chỉ định một MidpointRounding:

  • ToEven - Khi một số nằm giữa hai số khác, nó được làm tròn về phía số chẵn gần nhất.
  • AwayFromZero - Khi một số nằm giữa hai số khác, nó được làm tròn về phía số gần nhất cách xa số 0.

8
Và như tôi đã đề cập trong các chủ đề liên quan, hãy đảm bảo rằng bạn nhất quán trong cách làm tròn của mình - nếu đôi khi bạn làm tròn trong cơ sở dữ liệu và đôi khi trong .net, bạn sẽ gặp phải một lỗi lạ, một xu sẽ khiến bạn mất hàng tuần tìm ra.
chris

59
Một khách hàng đã từng trả cho tôi hơn 40.000 đô la để theo dõi lỗi làm tròn 0,11 đô la giữa hai con số chỉ bằng 1 BILLION đô la; $ 0,11 là do sự khác biệt trong lỗi làm tròn chữ số thứ 8 giữa máy tính lớn và Máy chủ SQL. Nói về một người cầu toàn!
EJ Brennan

12
@EJB - tôi có thể là người cầu toàn nếu tôi giao dịch với một tỷ đô la ;-)
royse41

12
@EJ Brennan: Bạn mất 40k đô la để tìm ra điều đó? Tôi thấy các vấn đề như thế này mọi lúc, làm tròn là nguyên nhân # 1, bình thường hóa kép / float là nguyên nhân # 2, lỗi lập trình # 3 - # 3 có thể được đặt ngay lập tức thành # 1 nếu không có trường hợp kiểm tra được xác định trước. btw bạn có thể vui lòng cho tôi liên lạc với khách hàng tỷ đô của bạn không, tôi nghĩ rằng tôi cũng có thể tìm thấy thêm một vài lỗi $ 40k trong hệ thống của mình! : D

3
@seanxe: Hoặc nếu bạn đã xem Office Space. Nghiêm túc mà nói, bất cứ khi nào bạn nhìn thấy sự thiếu chính xác bí ẩn, nhỏ bé về tiền bạc, giải quyết bí ẩn về chính xác cách chúng đang xảy ra hầu như luôn là một ý tưởng tốt. Có thể bạn sẽ quyết định không sửa lỗi, nhưng biết nguyên nhân cơ bản vẫn có giá trị. Tôi cá rằng nhiều người làm việc với tiền rất vui khi nhận thấy những điểm không chính xác nhỏ xíu.
Brian

22

Số thập phân chủ yếu được sử dụng cho tiền ; làm tròn ngân hàng là phổ biến khi làm việc với tiền . Hoặc bạn có thể nói.

Chủ yếu là các ngân hàng cần loại thập phân; do đó, nó làm tròn ngân hàng

Làm tròn ngân hàng có lợi thế là trung bình bạn sẽ nhận được kết quả tương tự nếu bạn:

  • làm tròn một tập hợp các dòng hóa đơn trên mạng trước khi thêm chúng vào,
  • hoặc thêm chúng lên sau đó làm tròn tổng số

Làm tròn trước khi thêm đã tiết kiệm rất nhiều công việc trong những ngày trước máy tính.

(Ở Anh khi chúng tôi đi ngân hàng thập phân sẽ không giao dịch với một nửa đồng xu, nhưng trong nhiều năm, vẫn có một nửa đồng xu và cửa hàng thường có giá kết thúc bằng một nửa - rất nhiều lần làm tròn)


8
"Số thập phân chủ yếu được sử dụng cho tiền" ... và mọi thứ khác không phải là số nguyên.
John Tyree

3
@JohnTyree, Không đúng trong hầu hết thời gian sử dụng double / float khi nó không phải là số nguyên. xem stackoverflow.com/questions/2545567/ Mạnh
Ian Ringrose 14/12/13

3
Ồ Lỗi vô lý về phía tôi. Decmials, vâng Số thập phân, không. Đối với hậu thế, tôi đồng ý với tình cảm ban đầu ở đây.
John Tyree

Các nhân viên ngân hàng có thể thích làm tròn số của các nhân viên ngân hàng, nhưng các nhân viên kế toán có thể không phải là người hâm mộ của nó, họ nói rằng 0,005 chênh lệch sẽ làm tròn số để làm tròn 0,01, không phụ thuộc vào số lẻ hay số chẵn.
JustAMartin

0

Sử dụng một hàm quá tải khác của hàm Round như thế này:

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

Nó sẽ xuất 3 . Và nếu bạn sử dụng

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

bạn sẽ nhận được làm tròn của ngân hàng.


4
Điều này không trả lời câu hỏi tại sao Làm tròn Ngân hàng được chọn làm mặc định.
shortthingsushi
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.