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

согласно документации,decimal.Round метод использует алгоритм round-to-even, который не является общим для большинства приложений. Поэтому я всегда пишу пользовательскую функцию, чтобы сделать более естественный алгоритм округления:

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

кто-нибудь знает причину этого рамочного проектного решения?

есть ли какая-либо встроенная реализация алгоритма round-half-up в рамках? Или, может быть, некоторые неуправляемые окна API-интерфейс?

Это может ввести в заблуждение новичков, которые просто пишут decimal.Round(2.5m, 0) ожидая 3 в результате, но получая 2 вместо этого.

5 ответов


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


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

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

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


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

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

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

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

Это в основном банкиры, которые нуждаются в тип decimal, поэтому он делает "банковское округление"

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

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

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

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


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

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

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

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

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