Tôi thấy rằng, trong C#, làm tròn một decimal
, theo mặc định, sử dụng MidpointRounding.ToEven
. Điều này được mong đợi, và là những gì C# spec quyết định. Tuy nhiên, do sau:Tại sao .NET decimal.ToString (chuỗi) làm tròn từ số 0, dường như không nhất quán với thông số ngôn ngữ?
- Một
decimal dVal
- Một định dạng
string sFmt
rằng, khi thông qua vàodVal.ToString(sFmt)
, sẽ dẫn đến một chuỗi chứa một phiên bản tròn củadVal
... nó là rõ ràng rằng decimal.ToString(string)
trả về giá trị được làm tròn bằng cách sử dụng MidpointRounding.AwayFromZero
. Điều này có vẻ là một mâu thuẫn trực tiếp của thông số C#.
Câu hỏi của tôi là: có lý do chính đáng trong trường hợp này không? Hay đây chỉ là sự mâu thuẫn trong ngôn ngữ?
Dưới đây, để tham khảo, tôi đã bao gồm một số mã viết để điều khiển một loại kết quả hoạt động làm tròn và kết quả hoạt động decimal.ToString(string)
, mỗi kết quả trên mọi giá trị trong một mảng giá trị decimal
. Các đầu ra thực tế được nhúng. Sau đó, tôi đã bao gồm một đoạn có liên quan từ phần Đặc tả Ngôn ngữ C# trên loại decimal
.
Đoạn mã ví dụ:
static void Main(string[] args)
{
decimal[] dArr = new decimal[] { 12.345m, 12.355m };
OutputBaseValues(dArr);
// Base values:
// d[0] = 12.345
// d[1] = 12.355
OutputRoundedValues(dArr);
// Rounding with default MidpointRounding:
// Math.Round(12.345, 2) => 12.34
// Math.Round(12.355, 2) => 12.36
// decimal.Round(12.345, 2) => 12.34
// decimal.Round(12.355, 2) => 12.36
OutputRoundedValues(dArr, MidpointRounding.ToEven);
// Rounding with mr = MidpointRounding.ToEven:
// Math.Round(12.345, 2, mr) => 12.34
// Math.Round(12.355, 2, mr) => 12.36
// decimal.Round(12.345, 2, mr) => 12.34
// decimal.Round(12.355, 2, mr) => 12.36
OutputRoundedValues(dArr, MidpointRounding.AwayFromZero);
// Rounding with mr = MidpointRounding.AwayFromZero:
// Math.Round(12.345, 2, mr) => 12.35
// Math.Round(12.355, 2, mr) => 12.36
// decimal.Round(12.345, 2, mr) => 12.35
// decimal.Round(12.355, 2, mr) => 12.36
OutputToStringFormatted(dArr, "N2");
// decimal.ToString("N2"):
// 12.345.ToString("N2") => 12.35
// 12.355.ToString("N2") => 12.36
OutputToStringFormatted(dArr, "F2");
// decimal.ToString("F2"):
// 12.345.ToString("F2") => 12.35
// 12.355.ToString("F2") => 12.36
OutputToStringFormatted(dArr, "###.##");
// decimal.ToString("###.##"):
// 12.345.ToString("###.##") => 12.35
// 12.355.ToString("###.##") => 12.36
Console.ReadKey();
}
private static void OutputBaseValues(decimal[] dArr)
{
Console.WriteLine("Base values:");
for (int i = 0; i < dArr.Length; i++) Console.WriteLine("d[{0}] = {1}", i, dArr[i]);
Console.WriteLine();
}
private static void OutputRoundedValues(decimal[] dArr)
{
Console.WriteLine("Rounding with default MidpointRounding:");
foreach (decimal d in dArr) Console.WriteLine("Math.Round({0}, 2) => {1}", d, Math.Round(d, 2));
foreach (decimal d in dArr) Console.WriteLine("decimal.Round({0}, 2) => {1}", d, decimal.Round(d, 2));
Console.WriteLine();
}
private static void OutputRoundedValues(decimal[] dArr, MidpointRounding mr)
{
Console.WriteLine("Rounding with mr = MidpointRounding.{0}:", mr);
foreach (decimal d in dArr) Console.WriteLine("Math.Round({0}, 2, mr) => {1}", d, Math.Round(d, 2, mr));
foreach (decimal d in dArr) Console.WriteLine("decimal.Round({0}, 2, mr) => {1}", d, decimal.Round(d, 2, mr));
Console.WriteLine();
}
private static void OutputToStringFormatted(decimal[] dArr, string format)
{
Console.WriteLine("decimal.ToString(\"{0}\"):", format);
foreach (decimal d in dArr) Console.WriteLine("{0}.ToString(\"{1}\") => {2}", d, format, d.ToString(format));
Console.WriteLine();
}
Đoạn từ phần 4.1.7 của C# Language Specification ("Các loại thập phân") (nhận được đầy đủ đặc tả here (.doc)):
Kết quả của phép toán đối với các giá trị kiểu thập phân là kết quả tính toán kết quả chính xác (tỷ lệ bảo toàn, được xác định cho mỗi toán tử) và sau đó làm tròn để khớp với biểu diễn. Kết quả được làm tròn thành giá trị thể hiện gần nhất, và khi kết quả bằng nhau gần hai giá trị có thể biểu thị, với giá trị có số chẵn ở vị trí chữ số nhỏ nhất (điều này được gọi là "làm tròn ngân hàng"). Kết quả bằng không luôn có dấu 0 và tỷ lệ là 0.
Thật dễ dàng để thấy rằng chúng có thể không đang xem xét ToString(string)
trong đoạn này, nhưng tôi có khuynh hướng nghĩ rằng nó phù hợp với mô tả này.
Có thể bạn nên xem xét rằng C# không có phương thức 'ToString (string)'. Khuôn khổ .NET. Tôi không chắc chắn rằng .NET Framework bị ràng buộc tuân thủ các quy tắc của bất kỳ ngôn ngữ lập trình cụ thể nào. –