Почему .NET использует банковское округление по умолчанию?

StackOverflow https://stackoverflow.com/questions/311696

  •  10-07-2019
  •  | 
  •  

Вопрос

Согласно документации, 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.

Это было полезно?

Решение

Возможно, потому что это лучший алгоритм. В течение многих выполненных округлений вы в среднем получите, что все .5 округляются одинаково вверх и вниз. Это дает более точные оценки фактических результатов, если вы, например, добавляете несколько округленных чисел. Я бы сказал, что хотя это и не то, что некоторые могут ожидать, это, вероятно, более правильная вещь.

Другие советы

Другой отвечает причинами, по которым алгоритм Банкира (он же округляет половину до четного ) это хороший выбор, вполне правильный. Он не страдает от отрицательного или положительного смещения так сильно, как метод округления на половину от нуля по наиболее разумным распределениям.

Но вопрос был в том, почему .NET по умолчанию использует фактическое округление Banker - и ответ заключается в том, что Microsoft следовала стандарт IEEE 754 . Это также упоминается в MSDN для Math.Round в разделе Примечания.

Также обратите внимание, что .NET поддерживает альтернативный метод, определенный IEEE, предоставляя перечисление MidpointRounding . Конечно, они могли бы предоставить больше альтернатив для решения связей, но они просто решили выполнить стандарт IEEE.

Хотя я не могу ответить на вопрос "Почему разработчики Microsoft выбрали это по умолчанию?", я просто хочу отметить, что дополнительная функция не нужна.

Math.Round позволяет вам указать MidpointRounding:

  • Четное - когда число находится на полпути между двумя другими, оно округляется в сторону ближайшего четного числа.
  • AwayFromZero - когда число находится на полпути между двумя другими, оно округляется в сторону ближайшего числа, которое находится вдали от нуля.

Десятичные дроби в основном используются для Деньги;банковское округление является обычным явлением при работе с Деньги.Или вы могли бы сказать.

Это в основном банкиры, что нужно десятичное тип;следовательно, это делает “банковское округление”

Преимущество банковского округления в том, что в среднем вы получите тот же результат, если:

  • округлите набор "строк счета” перед их суммированием,
  • или сложите их, а затем округлите общую сумму

Округление перед суммированием экономило много работы в те дни, когда еще не было компьютеров.

(В Великобритании, когда мы перешли на десятичную систему счисления, банки не имели дела с половиной пенса, но в течение многих лет все еще существовала монета в полпенса, и в магазинах часто цены заканчивались на половину пенса – так что было много округлений)

Используйте другую перегрузку функции Round, как это:

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

Будет выведено 3 . И если вы используете

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

вы получите банковское округление.

Кроме того, обратите внимание, что «округление» через строку формата (например, «0») получается результат, отличный от «Math.Round ()». А именно, что 5, .5 и т. Д. Всегда округляются:

let d, d' = 2.5, 3.5

Debug.WriteLine(Math.Round(d))      // 2.5 -> 2
Debug.WriteLine(d.ToString("0"))    // 2.5 -> 3

Debug.WriteLine(Math.Round(d'))     // 3.5 -> 4
Debug.WriteLine(d'.ToString("0"))   // 3.5 -> 4


let dd, dd' = 2.25, 2.35

Debug.WriteLine(Math.Round(dd, 1))     // 2.25 -> 2.2
Debug.WriteLine(dd.ToString("0.0"))    // 2.25 -> 2.3

Debug.WriteLine(Math.Round(dd', 1))    // 2.35 -> 2.4
Debug.WriteLine(dd'.ToString("0.0"))   // 2.35 -> 2.4
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top