Почему Math.Round (2.5) возвращает 2 вместо 3?
В C # результат Math.Round(2.5)
равен 2.
Предполагается, что это 3, не так ли? Почему это 2 вместо C #?
- Проверьте, является ли число целым
- Как вы можете округлить число до двух знаков после запятой в C #?
- Как округлить десятичное значение до двух знаков после запятой (для вывода на страницу)
- Как округлить Double до ближайшего Int в swift?
- Встроенный алгоритм .Net для округления значения до ближайшего 10 интервалов
- Круглая цифра в два раза
- Как округлить до ближайшего целого числа в C #
- Как округлить до 2 десятичных знаков с помощью Python?
- Число округлений в R до указанного количества цифр
- Как округлить время в T-SQL
- Круглый Двойной до ближайшей 10
- Повернуть поплавок в регулярную сетку предопределенных точек
- R раунд до ближайшего .5 или .1
Во-первых, это не будет ошибкой C # в любом случае – это будет ошибка .NET. C # – это язык – он не решает, как Math.Round
.
А во-вторых, нет – если вы прочтете документы , вы увидите, что округление по умолчанию равно «круглым до четного» (округление банкира):
Возвращаемое значение
Тип: System.Double
Целое число, ближайшее к. Если дробная составляющая a находится на полпути между двумя целыми числами, одна из которых четная, а другая нечетная, то четное число возвращается. Обратите внимание, что этот метод возвращаетDouble
вместо целочисленного типа.замечания
Поведение этого метода следует за стандартом IEEE 754, раздел 4. Этот вид округления иногда называют округлением до ближайшего или округления банкира. Это сводит к минимуму ошибки округления, возникающие в результате постоянного округления среднего значения в одном направлении.
Вы можете указать, как Math.Round
должен округлить средние точки, используя перегрузку, которая принимает значение MidpointRounding
. Есть одна перегрузка с помощью MidpointRounding
соответствующая каждой из перегрузок, которая не имеет одного:
-
Round(Decimal)
/Round(Decimal, MidpointRounding)
-
Round(Double)
/Round(Double, MidpointRounding)
-
Round(Decimal, Int32)
/Round(Decimal, Int32, MidpointRounding)
-
Round(Double, Int32)
/Round(Double, Int32, MidpointRounding)
Было ли это дефолт выбрано или нет, это другое дело. ( MidpointRounding
был представлен только в .NET 2.0. До этого я не уверен, что есть простой способ реализовать желаемое поведение, не делая этого самостоятельно.) В частности, история показала, что это не ожидаемое поведение – и в большинстве случаев это кардинальный грех в дизайне API. Я вижу, почему Rounding Banker полезен … но это все еще сюрприз для многих.
Вам может быть интересно взглянуть на ближайший эквивалентный enum ( RoundingMode
) Java, который предлагает еще больше опций. (Это касается не только средних точек).
Это называется округлением до четного (или округления банкира), что является действительной страtagsей округления для минимизации начисленных ошибок в суммах (MidpointRounding.ToEven)
. Теория состоит в том, что если вы всегда будете округлять 0,5 числа в одном направлении, ошибки будут нарастать быстрее (от округлой до четности предполагается минимизировать это) (a) .
Следуйте этим ссылкам для описания MSDN:
-
Math.Floor
, которая округляется до отрицательной бесконечности. -
Math.Ceiling
, которая округляется до положительной бесконечности. -
Math.Truncate
, который округляется вверх или вниз к нулю. -
Math.Round
, которая округляется до ближайшего целого числа или заданного числа десятичных знаков. Вы можете указать поведение, если оно точно равноудалено между двумя возможностями, например округлением, чтобы окончательная цифра была четной («Round(2.5,MidpointRounding.ToEven)
» становитсяRound(2.5,MidpointRounding.ToEven)
2) или так, что он находится дальше от нуля («Round(2.5,MidpointRounding.AwayFromZero)
“становится 3).
Следующая диаграмма и таблица могут помочь:
-3 -2 -1 0 1 2 3 +--|------+---------+----|----+--|------+----|----+-------|-+ abcde a=-2.7 b=-0.5 c=0.3 d=1.5 e=2.8 ====== ====== ===== ===== ===== Floor -3 -1 0 1 2 Ceiling -2 0 1 2 3 Truncate -2 0 0 1 2 Round(ToEven) -3 0 0 2 3 Round(AwayFromZero) -3 -1 0 2 3
Обратите внимание, что Round
намного мощнее, чем кажется, просто потому, что он может округляться до определенного количества десятичных знаков. Все остальные округляются до нуля десятичными знаками. Например:
n = 3.145; a = System.Math.Round (n, 2, MidpointRounding.ToEven); // 3.14 b = System.Math.Round (n, 2, MidpointRounding.AwayFromZero); // 3.15
С помощью других функций вы должны использовать метод умножения / деления для достижения такого же эффекта:
c = System.Math.Truncate (n * 100) / 100; // 3.14 d = System.Math.Ceiling (n * 100) / 100; // 3.15
(а) Конечно, эта теория зависит от того, что ваши данные имеют довольно равномерное распределение значений по ровным половинам (0,5, 2,5, 4,5, …) и нечетным половинам (1,5, 3,5, …).
Если все «половинные значения» равны (например), ошибки будут накапливаться так же быстро, как если бы вы всегда округлялись.
Из MSDN возвращается Math.Round (double a) :
Целое число, ближайшее к. Если дробная составляющая a находится на полпути между двумя целыми числами, одна из которых четная, а другая нечетная, то четное число возвращается.
… и так 2.5, находясь на полпути между 2 и 3, округляется до четного числа (2). это называется Rounding Banker (или round-to-even) и является обычно используемым стандартом округления.
Такая же статья MSDN:
Поведение этого метода следует за стандартом IEEE 754, раздел 4. Этот вид округления иногда называют округлением до ближайшего или округления банкира. Это сводит к минимуму ошибки округления, возникающие в результате постоянного округления среднего значения в одном направлении.
Вы можете указать другое поведение округления, вызывая перегрузки Math.Round, которые принимают режим MidpointRounding
.
Вы должны проверить MSDN для Math.Round
:
Поведение этого метода следует за стандартом IEEE 754, раздел 4. Этот вид округления иногда называют округлением до ближайшего или округления банкира.
Вы можете указать поведение Math.Round
с использованием перегрузки:
Math.Round(2.5, 0, MidpointRounding.AwayFromZero); // gives 3 Math.Round(2.5, 0, MidpointRounding.ToEven); // gives 2
Характер округления
Рассмотрим задачу округления числа, содержащего долю, например, целого числа. Процесс округления в этом случае заключается в том, чтобы определить, какое целое число наилучшим образом соответствует числу, которое вы округливаете.
В общем или «арифметическом» округлении ясно, что 2.1, 2.2, 2.3 и 2.4 округляются до 2.0; и 2,6, 2,7, 2,8 и 2,9 – 3,0.
Это оставляет 2.5, что не ближе к 2.0, чем к 3.0. Это зависит от вас, чтобы выбрать между 2.0 и 3.0, либо будет одинаково справедливым.
Для минус-чисел -2,1, -2,2, -2,3 и -2,4 будет -2,0; и -2,6, 2,7, 2,8 и 2,9 составляли -3,0 при арифметическом округлении.
Для -2.5 необходим выбор между -2.0 и -3.0.
Другие формы округления
«Округление» принимает любое число с десятичными знаками и делает его следующим «целым» числом. Таким образом, не только 2,5 и 2,6 раунда до 3,0, но и 2,1 и 2,2.
Округление перемещает как положительные, так и отрицательные числа от нуля. Например. От 2,5 до 3,0 и от -2,5 до -3,0.
«Округление» сокращает число, сокращая ненужные цифры. Это приводит к перемещению чисел к нулю. Например. От 2,5 до 2,0 и от -2,5 до -2,0
В «округлении банкира» – в его наиболее распространенной форме – округленная .5 округляется либо вверх, либо вниз, так что результат округления всегда является четным числом. Таким образом, 2,5 раунда до 2,0, от 3,5 до 4,0, от 4,5 до 4,0, от 5,5 до 6,0 и т. Д.
«Альтернативное округление» чередует процесс для любого .5 между округлением и округлением.
«Случайное округление» округляет вверх .5 вверх или вниз на совершенно случайной основе.
Симметрия и асимметрия
Функция округления называется «симметричной», если она либо округляет все числа от нуля, либо округляет все числа к нулю.
Функция является «асимметричной», если округленные положительные числа к нулю и отрицательные числа от нуля. Например. 2,5-2,0; и от -2,5 до -3,0.
Также асимметрична функция, которая округляет положительные числа от нуля и отрицательных чисел к нулю. Например. От 2,5 до 3,0; и от -2,5 до -2,0.
Большую часть времени люди думают о симметричном округлении, где -2,5 будут округлены до -3,0 и 3,5 будут округлены до 4,0. (в C # Round(AwayFromZero)
)
Значение по умолчанию MidpointRounding.ToEven
или округление банкиров ( 2.5 становится 2, 4.5 становится MidpointRounding.ToEven
4 и т. Д. ) Уже MidpointRounding.ToEven
меня, написав отчеты для бухгалтерского учета, поэтому я напишу несколько слов о том, что я узнал, ранее и от поиска в него для этого поста.
Кто такие банкиры, которые округляются по четным числам (возможно, британские банкиры!)?
Из Википедии
Происхождение термина округления банкиров остается более неясным. Если этот метод округления всегда был стандартом в банковской деятельности, доказательства оказались чрезвычайно трудными для поиска. Напротив, раздел 2 доклада Европейской комиссии «Введение евро и округление валютных сумм» предполагает, что ранее не было стандартного подхода к округлению банковского дела; и он указывает, что суммы «на полпути» должны быть округлены.
Это кажется очень странным способом округления, особенно для банковских операций, если, конечно, банки не используют, чтобы получать много депозитов в равных количествах. Депозит £ 2,4 млн, но мы назовем его 2 млн фунтов стерлингов.
Стандарт IEEE 754 относится к 1985 году и дает оба способа округления, но с рекомендациями банкира, рекомендованные стандартом. В этой статье в википедии есть длинный список того, как языки реализуют округление (исправьте меня, если какой-либо из нижеперечисленных ошибок), и большинство из них не используют банкиров, но округление, которое вы преподаете в школе:
- C / C ++ round () из math.h раундов от нуля (не округление банкира)
- Java Math.Round округляется от нуля (он наносит результат, добавляет 0.5, отбрасывает целое число). В BigDecimal есть альтернатива
- Perl использует аналогичный подход к C
- Javascript такой же, как Java Math.Round.
Из MSDN:
По умолчанию Math.Round использует MidpointRounding.ToEven. Большинство людей не знакомы с «округлением до единого», поскольку альтернатива, «округление от нуля», чаще всего преподается в школе. .NET по умолчанию имеет значение «округление до четного», поскольку оно статистически превосходит, поскольку оно не разделяет тенденцию «округления от нуля» к округлению немного чаще, чем округляет его (при условии, что округленные числа имеют тенденцию быть положительными. )
http://msdn.microsoft.com/en-us/library/system.math.round.aspx
Поскольку Silverlight не поддерживает параметр MidpointRounding, вам нужно написать свой собственный. Что-то вроде:
public double RoundCorrect(double d, int decimals) { double multiplier = Math.Pow(10, decimals); if (d < 0) multiplier *= -1; return Math.Floor((d * multiplier) + 0.5) / multiplier; }
Для примеров, в том числе о том, как использовать это расширение, см. Сообщение: .NET и Silverlight Rounding
У меня была эта проблема, когда мой SQL-сервер округлялся от 0,5 до 1, пока мое приложение C # этого не делало. Таким образом, вы увидите два разных результата.
Вот реализация с int / long. Вот как работает Java.
int roundedNumber = (int)Math.Floor(d + 0.5);
Это, вероятно, самый эффективный метод, о котором вы могли бы подумать.
Если вы хотите сохранить его в два раза и использовать десятичную точность, то это действительно вопрос использования показателей из 10 в зависимости от того, сколько десятичных знаков.
public double getRounding(double number, int decimalPoints) { double decimalPowerOfTen = Math.Pow(10, decimalPoints); return Math.Floor(number * decimalPowerOfTen + 0.5)/ decimalPowerOfTen; }
Вы можете ввести отрицательное десятичное число для десятичных точек, и это слово также отлично.
getRounding(239, -2) = 200
У этой записи есть ответ, который вы ищете:
http://weblogs.asp.net/sfurman/archive/2003/03/07/3537.aspx
В основном это то, что он говорит:
Возвращаемое значение
Число ближайшего значения с точностью, равной цифрам. Если значение находится на полпути между двумя числами, одно из которых четное, а другое – нечетное, то возвращается четное число. Если точность значения меньше цифр, тогда значение возвращается без изменений.
Поведение этого метода следует за стандартом IEEE 754, раздел 4. Этот вид округления иногда называют округлением до ближайшего или округления банкира. Если цифры равны нулю, этот вид округления иногда называется округлением к нулю.
Silverlight не поддерживает параметр MidpointRounding. Ниже приведен метод расширения для Silverlight, который добавляет перечисление MidpointRounding:
public enum MidpointRounding { ToEven, AwayFromZero } public static class DecimalExtensions { public static decimal Round(this decimal d, MidpointRounding mode) { return d.Round(0, mode); } /// /// Rounds using arithmetic (5 rounds up) symmetrical (up is away from zero) rounding /// /// A Decimal number to be rounded. /// The number of significant fractional digits (precision) in the return value. /// The number nearest d with precision equal to decimals. If d is halfway between two numbers, then the nearest whole number away from zero is returned. public static decimal Round(this decimal d, int decimals, MidpointRounding mode) { if ( mode == MidpointRounding.ToEven ) { return decimal.Round(d, decimals); } else { decimal factor = Convert.ToDecimal(Math.Pow(10, decimals)); int sign = Math.Sign(d); return Decimal.Truncate(d * factor + 0.5m * sign) / factor; } } }
Источник: http://anderly.com/2009/08/08/silverlight-midpoint-rounding-solution/
использование пользовательского округления
public int Round(double value) { double decimalpoints = Math.Abs(value - Math.Floor(value)); if (decimalpoints > 0.5) return (int)Math.Round(value); else return (int)Math.Floor(value); }
Простой способ:
Math.Ceiling(decimal.Parse(yourNumber + ""));
Вот как я должен был работать вокруг:
Public Function Round(number As Double, dec As Integer) As Double Dim decimalPowerOfTen = Math.Pow(10, dec) If CInt(number * decimalPowerOfTen) = Math.Round(number * decimalPowerOfTen, 2) Then Return Math.Round(number, 2, MidpointRounding.AwayFromZero) Else Return CInt(number * decimalPowerOfTen + 0.5) / 100 End If End Function
Попытка с 1.905 с двумя десятичными знаками даст 1.91, как ожидалось, но Math.Round(1.905,2,MidpointRounding.AwayFromZero)
дает 1.90! Метод Math.Round абсолютно несогласован и непригоден для большинства проблем, с которыми могут столкнуться программисты. Я должен проверить if (int) 1.905 * decimalPowerOfTen = Math.Round(number * decimalPowerOfTen, 2)
потому что я не хочу округлять то, что должно быть округлено.
Это уродливо, как и все ад, но всегда производит правильное арифметическое округление.
public double ArithRound(double number,int places){ string numberFormat = "###."; numberFormat = numberFormat.PadRight(numberFormat.Length + places, '#'); return double.Parse(number.ToString(numberFormat)); }