Почему .NET по умолчанию использует банковское округление? - PullRequest
260 голосов
/ 22 ноября 2008

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

422 голосов
/ 03 июля 2011

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

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

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

188 голосов
/ 22 ноября 2008

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

86 голосов
/ 22 ноября 2008

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

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

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

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

В основном банкирам нужны десятичный тип; поэтому это делает «Банковское округление»

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

  • округлить набор «строк счета-фактуры» перед их сложением,
  • или сложите их, затем округлите общее количество

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

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

0 голосов
/ 26 марта 2017

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

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

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

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

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

...