Почему Convert.ToInt32() округляется до ближайшего четного числа, а не ближайшего целого числа?
В документации msdn для Convert.ToInt32()
указано:
Если значение находится на полпути между двумя целыми числами, четное число равно вернулся; то есть 4.5 преобразуется в 4, а 5.5 преобразуется в 6.
http://msdn.microsoft.com/en-us/library/ffdk7eyz.aspx
Почему это?
Конечно, было бы логичнее округлить до ближайшего целого числа, не так ли? Если это так, 4.5 станет 5, а 5.5 станет 6, что кажется более интуитивным.
Ответы
Ответ 1
В разделе History of the Wikipedia для Rounding есть некоторые утверждения о роли "round to even" в вычисления. Интересно, что, похоже, "Roundings Bankers" не имеет достаточных доказательств того, что оно является официальным в каком-либо смысле этого слова, поэтому его можно записать только как терминологию сленга.
Это только "более логично", если вы подписаны на этот механизм округления. Банковское округление (которое является дефолтом в этом случае) также вполне логично.
Представьте, что если банки округлятся до ближайшего копейки за каждую дробную сумму, они будут делать намного меньше (теряют много, за циничные) деньги с миллионами миллионов транзакций, которые обрабатываются ежедневно. ОК, поэтому этот пример циничен.
Переход к ближайшему четному номеру (или нечетному, но история выбрала иначе) означает, что не каждое разрешение округления увеличивается, некоторые из них теперь могут опускаться. Когда вы ставите это на закон средних значений, оно становится справедливым решением для использования , кто несет ответственность за выплату дополнительной половины пенни.
Что касается того, почему это было выбрано для рамки, этот вопрос пытается решить эту проблему:
Почему .NET использует округление банкира по умолчанию?
Конечно, все это возвращается к финансовым дням, и его применимость к целым числам может быть поставлена под сомнение, но зачем беспокоиться? Примите его, переопределите, если хотите, просто поймите, как это работает.
Для людей интересно, как изменить округление по умолчанию:
Если вы предоставляете нецелое число Convert.ToInt32
, вам сначала нужно будет сделать что-то вроде Convert.ToDouble
, а затем Math.Round
с помощью перегрузка для изменения логики округления.
Ответ 2
Именно поэтому MidpontRounding
перегрузка была добавлена в Math.Round
.
Поэтому для правильного округления вы должны использовать Math.Round, а не Convert.ToInt32.
Ответ 3
Без учета субъективного вопроса о том, будет ли MidpointRounding.ToEven(округление Banker) или MidpointRounding.AwayFromZero лучшим по умолчанию.
При разработке этого Microsoft предпочла бы, что языки .NET были созданы для замены.
-
По умолчанию VB classic всегда использовал округление Banker.
-
Преобразование C/С++ усекается при кастинге и имеет библиотечные функции floor() и ceil() в библиотеке времени исполнения, но (AFAIK, может быть, неверно) нет круглой функции.
-
Java имеет Math.round, что в документации описывается как эквивалент Math.round(a + 0,5). Вероятно, это не то, что большинство людей ожидали бы от отрицательных чисел (от -3,5 до -3).
-
Разработчики VB, возможно, будут нуждаться в большей поддержке рук, чем разработчики из C/С++ или Java.
Следовательно, представляется разумным, что при проектировании .NET библиотека классов предоставляла методы Floor
, Ceiling
и Round
и что поведение Round по умолчанию будет иметь поведение VB.
Также представляется разумным, что Convert.ToInt32() будет использовать метод Round (хотя я предполагаю, что случай может быть сделан для Floor, для согласованности с кастингом).
Ответ 4
Если вам нужно это поведение, вам нужно использовать Math.Round
и указать MidpointRounding.AwayFromZero
.
Например:
int result = (int)Math.Round(4.5, MidpointRounding.AwayFromZero);
Демо: http://ideone.com/ZAbBL
Convert.ToInt32(double)
не использует Math.Round
сам, вместо этого он реализован таким образом (ILSpy):
public static int ToInt32(double value)
{
if (value >= 0.0)
{
if (value < 2147483647.5)
{
int num = (int)value;
double num2 = value - (double)num;
if (num2 > 0.5 || (num2 == 0.5 && (num & 1) != 0))
{
num++;
}
return num;
}
}
else
{
if (value >= -2147483648.5)
{
int num3 = (int)value;
double num4 = value - (double)num3;
if (num4 < -0.5 || (num4 == -0.5 && (num3 & 1) != 0))
{
num3--;
}
return num3;
}
}
throw new OverflowException(Environment.GetResourceString("Overflow_Int32"));
}