Как округлить число в с
Перейти к содержимому

Как округлить число в с

Округление к целому в .NET

Все мы знаем, что такое округление. Если кто-то забыл, то округление — это замена числа на его приближённое значение, записанное с меньшим количеством значащих цифр. Если спросить человека с ходу, что получится при округлении 6,5 до целых, он не задумываясь ответит «7». Нас со школы учили, что числа округляются до ближайшего целого большего по модулю числа. То есть, если в округляемом числе дробная часть равна или больше половине разряда целой части, то мы округляем исходное число до ближайшего большего.

Проще говоря:
и т.д.

И вот, выходя из школы и становясь программистами мы зачастую ожидаем того же поведения от наших мощных языков программирования. Совсем забывая, что в школе нас учили «математическому округлению», а на самом деле видов этих округлений намного больше. На одной только википедии можно нарыть вот сколько вариантов округления 0,5 к ближайшему целому числу:

  • Математическое округление
  • Случайное округление
  • Чередующееся округление
  • Банковское округление

А вот «банковское округление» — это уже интересненько. «Почему?» — спросите вы. В дотнете мы часто используем класс Convert, который предоставляет уйму методов для конвертации одного типа данных в другие (не путать с приведением, о нем будет ниже). И вот, оказывается, что при конвертации чисел с плавающей запятой (double, float, decimal) в целочисленный тип int через метод Convert.ToInt32 под капотом работает «банковское» округление. Оно тут используется по умолчанию!

И вроде как незнание этой мелочи сильно не сказывается на вашей работе, но как только вам приходится работать со статистикой и расчетами показателей, базирующихся на куче всяких записей и циферок эта штука вылазит боком. Потому что мы ожидаем (от незнания), что все наши конвертации\округления в расчетах будут работать по правилам «математического» округления. И смотрим как баран на новые ворота на результат округления 6,5, который равен 6.

Первая мысль программиста, который это видит — «Возможно округление работает в обратную сторону, и по правилам округляется до наименьшего числа?», «Может я что-то забыл из школьной математики?». Дальше он идет в google и понимает что, ничего не забыли, и что творится какая-то чернь. На этом шаге ленивый разработчик решит, что это стандартное поведение метода Convert.ToInt32, округлять до наименьшего целого, и забьет на дальнейший поиск. И будет думать, что если Convert.ToInt32(6,5) = 6, то по аналогии Convert.ToInt32(7,5) = 7. Но не тут-то было. Таких разработчиков в дальнейшем судьба бьет по голове пачкой багов от отдела QA.

Дело в том, что «банковское» округление работает чуть хитрее — оно округляет число до ближайшего четного целого числа, а не до ближайшего целого по модулю. Этот тип округления якобы более честный в случае применения в банковских операциях — банки не будут обделять ни себя ни клиентов, из расчета, что операций с четной целой частью, сколько же, сколько и операций с нечетной целой частью. Но как по мне — всё равно мутновато 🙂 Так вот, именно поэтому Convert.ToInt32(6,5) даст результат 6, а результат для Convert.ToInt32(7,5) будет равен 8, а не 7 🙂

Что же делать, что бы получить всем привычное «математическое» округления? У методов класса Convert нет дополнительных настроек округления. Оно и верно, ибо класс этот служит в первую очередь не для округления, а для конвертации типов. На помощь нам приходит замечательный класс Math с его методом Round. Но тут тоже будьте аккуратны, ибо по умолчанию этот метод работает так же как и округление в Convert.ToInt32() — по «банковскому» правилу. Однако, это поведение можно изменять с помощью второго аргумента, входящего в метод Round. Так, Math.Round(someNumber, MidpointRounding.ToEven) даст нам дефолтовое «банковское» округление. А вот Math.Round(someNumber, MidpointRounding.AwayFromZero) будет работать по привычным правилам «математического» округления.

И кстати, Convert.ToInt32() не использует под капотом System.Math.Round(). Специально нарыл на github реализацию этого метода — округление считается по остаткам:

И напоследок пару слов о приведении типов:

В этом примере я привожу тип с плавающей запятой (double в данном случае) к целочисленному int. Так вот, при приведении типов к целочисленному вся не целая часть просто отсекается. Соответственно, в данном примере в переменной «intNumber» будет лежать число 6. Никаких правил округления тут нет, просто отсечение всего, что идет после запятой. Помните об этом!

Учимся округлять в C#

А знаете ли вы, что Math.Round(1.5) == Math.Round(2.5) == 2 ? Можете ли сходу сказать, сколько будет -7%3 и 7%-3 ? Помните ли, чем отличаются Math.Round , Math.Floor , Math.Ceiling , Math.Truncate ? А как происходит округление при использовании string.Format ? Давайте немного погрузимся в мир округлений и разберёмся с нюансами, которые не для всех могут быть очевидными.

Math.Round

Math.Round — это метод округления к ближайшему числу или к ближайшему числу с заданным количеством знаков после запятой. Работает с типами decimal и double , в параметрах можно встретить три вида параметров:

  • value : округляемое число
  • digits : количество знаков в дробной части, которые нужно оставить
  • mode : параметр, который определяет в какую сторону округлять число, которое находится ровно посередине между двумя вариантами

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

  • AwayFromZero : округление происходит к тому числу, которое дальше от нуля.
  • ToEven : округление происходит к чётному числу.

Обратите внимание, что по умолчанию mode == MidpointRounding.ToEven , поэтому Math.Round(1.5) == Math.Round(2.5) == 2 .

Math.Floor, Math.Ceiling, Math.Truncate

  • Math.Floor округляет вниз по направлению к отрицательной бесконечности.
  • Math.Ceiling округляет вверх по направлению к положительной бесконечности.
  • Math.Truncate округляет вниз или вверх по направлению к нулю.

Сводная таблица

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

Округление проводится в соответствии со стандартом IEEE Standard 754, section 4.

Целочисленное деление и взятие по модулю

В C# есть два замечательных оператора над целыми числами: / для целочисленного деления (MSDN) и % для взятия остатка от деления (MSDN). Деление производится по следующим правилам:

Округление десятичных значений в C#


Тип данных decimal используется для хранения чисел с высоким уровнем точности. Потенциально большое количество знаков после запятой полезно для многих математических или статистических расчетов. Тем не менее, иногда необходимо и округлять эти значения. Эта проблема не так часто волнует разработчиков, пишущих программы на заказ, но для студентов или самостоятельно изучающих язык C# статья ниже может оказаться весьма полезной.

Округление значения

C# предоставляет три собственные числовые типы данных для хранения значений с плавающей запятой. К ним относятся одинарной точности, двойной точности и десятичные числа, которые объявляются с помощью «float», «double» и «decimal» ключевых слов соответственно. Каждый из этих типов имеет различный диапазон возможных значений с различной степенью точности. Тип » float » является наименее точным с наименьшим диапазоном значений. Тип «decimal» имеет самый большой диапазон и наибольшую точность. Выбор типа значения для использования в любых целях является компромиссом между требованиями размера и точности чисел и объемом памяти, используемой объявленной переменной.

В этой статье мы будем работать с типом данных decimal, но акцентируем внимание не на его размере и точности, а на дополнительной функциональности, присущей этому типу в отличие от двух других вариантов. В частности, мы будем использовать четыре статических метода округления, которые доступны только для десятичных значений. Конечно, это можно использовать и для одно- и двойной чисел точности тоже. Однако в этих случаях необходимо сначала привести к или преобразовать значение в decimal или использовать члены класса Math.

Обратите внимание: Методы, описанные в этой статье, требуют .NET Framework версии 2.0 или более поздней версии. Некоторые из функций доступны в .NET 1.1, но там использование может быть ограничено.

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

Floor и Ceiling

Статические методы Floor и Ceiling выполняют аналогичные функции усечения: они удаляют дробную часть числа и возвращают целое число. Опять же целое число возвращается в десятичной структуре. Основное различие между этими методами и Truncate является то, что направление округления фиксируется как для положительных и отрицательных чисел. Для функции Floor округление всегда вниз, а Ceilin всегда вверх, как можно видеть в результатах примере кода ниже.

Контролируемые округления

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

В заключительной части этой статьи мы рассмотрим метод Round более подробно.

Простое округление

Самый простой способ, которым можно использовать метод Round, это с одним параметром, содержащим число, которое надо изменить. При использовании таким образом возвращаемое значение содержит целое число, которое ближе всего к переданному значению.

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

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

Правила медианы

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

Чтобы задать правило, параметр MidpointRounding добавляется к вызову метода.

Округление до указанного количества десятичных знаков

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

Здесь правило медианы по умолчанию «ToEven» применяется для округления, за исключением того, что конченая цифра округляется до ближайшего четного числа.

Вы можете изменить правило медианы, используется при округлении до количества знаков после запятой. Чтобы сделать это, просто добавьте значение MidpointRounding в качестве третьего параметра:

Примечание из msdn.microsoft.com к Math.Round

Таким образом, в статье мы попробовали немного прояснить ситуацию с округлением decimal. Как видим, все не так уж и сложно, но есть нюансы, которые необходимо иметь в виду при написании программ. Кстати, программы на заказ пишу и я. Недорого, быстро и с гарантией.


Автор этого материала — я — Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML — то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.

тегистатьи IT, си шарп, округление, decimal

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *