2012-02-10 31 views
23

Trong .NET, tại sao System.Math.Round(1.035, 2, MidpointRounding.AwayFromZero) sản lượng 1,03 thay vì 1,04? Tôi cảm thấy như câu trả lời cho câu hỏi của tôi nằm trong phần có nhãn "Ghi chú cho người gọi" tại http://msdn.microsoft.com/en-us/library/ef48waz8.aspx, nhưng tôi không thể quấn đầu của tôi xung quanh lời giải thích.Tại sao System.MidpointRounding.AwayFromZero không làm tròn trong trường hợp này?

+0

Điều thú vị là, gọi phương pháp này với giá trị là 1,135 nhuận 1,14. –

+10

"Lưu ý" về bản chất là số cơ sở-2. Nó không thể đại diện cho một số giá trị * chính xác * Bạn gõ 1.035, biểu diễn bên trong có thể là 1.034999999982 hoặc bất kỳ giá trị nào. Nếu bạn quan tâm đến việc biểu diễn chính xác các chữ số cho một số vị trí thập phân nhất định, có lẽ System.Decimal là loại dành cho bạn. Đặc biệt như vậy nếu bạn đang đối phó với các giá trị tài chính. –

+1

@KyleTrauberman đó là bởi vì với 1.135 bạn nhận được may mắn- đất xấp xỉ cao hơn một chút so với 1.135, ngược với mức thấp hơn như trong OP. –

Trả lời

26

nghi ngờ của bạn là hoàn toàn đúng. Các số có phần phân số, khi được biểu diễn dưới dạng chữ trong .NET, theo mặc định là doubles. Một đôi (giống như một phao) là một xấp xỉ của một giá trị thập phân, không phải là một giá trị thập phân chính xác. Nó là giá trị gần nhất có thể được biểu diễn trong base-2 (nhị phân). Trong trường hợp này, sự gần đúng bao giờ biến mất trên mặt nhỏ của 1.035. Nếu bạn viết nó sử dụng một số thập phân rõ ràng nó hoạt động như bạn mong đợi:

Console.WriteLine(Math.Round(1.035m, 2, MidpointRounding.AwayFromZero)); 
Console.ReadKey(); 

Để hiểu tại sao đôi và nổi làm việc theo cách họ làm, hãy tưởng tượng đại diện cho số 1/3 trong số thập phân (hoặc nhị phân, mà phải chịu đựng từ cùng một vấn đề). Bạn không thể dịch nó thành .3333333 ...., có nghĩa là đại diện cho nó chính xác sẽ đòi hỏi một lượng bộ nhớ vô hạn.

Máy tính sử dụng tính năng xấp xỉ này. Tôi sẽ giải thích chính xác làm thế nào, nhưng tôi có thể làm cho nó sai. Bạn có thể đọc tất cả về nó ở đây mặc dù: http://en.wikipedia.org/wiki/IEEE_754-1985

+0

Lưu ý, mặc định là * double. * 1.0 hoàn toàn là gấp đôi, mặc dù nó có thể rõ ràng như vậy với 1d hoặc 1.0d. Hậu tố 'f' là bắt buộc đối với các ký tự float. –

+0

Cố định, nhờ Anthony –

+0

@ChrisShain đôi thực sự là một giá trị chính xác; nó chỉ là một giá trị nhị phân chính xác, có thể hoặc không thể xấp xỉ với giá trị thập phân chính xác được chỉ ra bởi chữ. Ví dụ, giá trị nhị phân '0.25d' chính xác bằng giá trị thập phân 0,25. Plus, tôi nghĩ bạn có nghĩa là "tưởng tượng đại diện cho số 1/3 trong thập phân" bởi vì 0.33333 ... là đại diện thập phân, không phải nhị phân. Biểu diễn nhị phân giống như 0,0101010101 ... – phoog

1

Giả sử tôi nói rằng nội bộ 1.035 không thể được biểu diễn dưới dạng nhị phân chính xác 1.035 và có thể là (dưới mui xe) 1.0349999999999999, đó là lý do tại sao nó tròn xuống.

Chỉ cần đoán.

3

Tôi tin rằng ví dụ bạn đang đề cập đến là một vấn đề khác; theo như tôi hiểu họ đang nói rằng 0,1 không được lưu trữ, trong phao, như chính xác 0,1, nó thực sự hơi tắt vì làm thế nào nổi được lưu trữ trong nhị phân. Vì vậy, giả sử nó thực sự trông giống như 0.0999999999999 (hoặc tương tự), một cái gì đó rất, rất ít ít hơn 0,1 - vì vậy hơi rằng nó không có xu hướng tạo ra nhiều sự khác biệt. Vâng, không, họ đang nói: một sự khác biệt đáng chú ý là việc thêm số này vào số của bạn và làm tròn sẽ thực sự đi sai hướng bởi vì mặc dù các con số rất gần nhưng nó vẫn được coi là "nhỏ hơn" .5 để làm tròn .

Nếu tôi hiểu lầm trang đó, tôi hy vọng ai đó sửa chữa cho tôi :)

Tôi không thấy như thế nào nó liên quan đến cuộc gọi của bạn, tuy nhiên, bởi vì bạn đang là rõ ràng hơn. Có lẽ nó chỉ lưu trữ số của bạn theo cách tương tự.

+0

Đúng vậy. –

+0

+1 cho "vì cách lưu trữ nổi trong nhị phân". – phoog

6

I'ts vì biểu diễn nhị phân của 1,035 gần gũi hơn với 1,03 so với 1,04

Để có kết quả tốt hơn làm điều đó theo cách này -

decimal result = decimal.Round(1.035m, 2, MidpointRounding.AwayFromZero); 
7

Các đại diện nhị phân của 1.035d là 0x3FF08F5C28F5C28F, mà trên thực tế là 1.03499999999999992006394222699E0, vì vậy System.Math.Round (1,035, 2, MidpointRounding.AwayFromZero) mang lại 1,03 thay vì 1.04 , đúng vậy.

Tuy nhiên, biểu diễn nhị phân của 4.005d là 0x4010051EB851EB85, là 4.00499999999999989341858963598, vì vậy System.Math.Round (4.005, 2, MidpointRounding.AwayFromZero) sẽ mang lại 4.00, nhưng nó mang lại 4.01 là sai (hoặc thông minh) sửa chữa'). Nếu bạn kiểm tra nó trong MS SQL chọn ROUND (CAST (4.005 AS float), 2), nó là 4.00 Tôi không hiểu tại sao .NET áp dụng 'sửa lỗi thông minh' này khiến mọi thứ trở nên tồi tệ hơn.

Bạn có thể kiểm tra biểu diễn nhị phân của một đôi tại địa chỉ: http://www.binaryconvert.com/convert_double.html

Các vấn đề liên quan