Всем бородатое ку, товарищи!

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

Проще говоря:
6,4 = 6 
6,5 = 7 
6,6 = 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 реализацию этого метода — округление считается по остаткам:

public static int ToInt32(double value) {
	if (value >= 0) {
		if (value < 2147483647.5) {
			int result = (int)value;
			double dif = value - result;
			if (dif > 0.5 || dif == 0.5 && (result & 1) != 0) result++;
			return result;
		}
	}
	else {
		if (value >= -2147483648.5) {
			int result = (int)value;
			double dif = value - result;
			if (dif < -0.5 || dif == -0.5 && (result & 1) != 0) result--;
			return result;
		}
	}
	throw new OverflowException(Environment.GetResourceString("Overflow_Int32"));
}     

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

var number = 6.9;
var intNumber = (int)number;

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

Ссылки по теме:


P.S. Спасибо Максиму Якушкину за то, что обратил внимание на этот неявный момент.

P.P.S. Кстати, в python округление по дефолту работает так же по «банковскому» принципу. Возможно, в вашем языке такая же штука, будьте бдительны с числами :)

Комментарии (61)


  1. Ketovdk
    02.08.2019 16:24
    -1

    в целом округлять таким образом не стоит, потому-что во многих языках нет гарантии, что 6.5 — это на самом деле 6.5, а не, например 6.4999999999999 за счет погрешности округления. Ну вернее в этом случае гарантия есть, но в других может и не быть, так-что старое доброе round(x+0.0000001) работает во всех (почти) случаях


    1. alex_zzzz
      02.08.2019 19:25

      6,5 — это всегда 6,5. И десятичном виде, и в двоичном.


      1. LeX_KaR Автор
        02.08.2019 19:33

        Видимо имелось ввиду поведение JS в некоторых ситуациях после проведения мат операций.


        1. storm_r1der
          02.08.2019 20:54

          Есть `BigInt`, который решает эту проблему. Если точность операции критична — нужно использовать сторонние библиотеки или делать свое решение, если в языке его нет.


      1. greg123
        02.08.2019 20:17

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


        1. slonopotamus
          03.08.2019 15:21

          Как вот это вот всё что вы сказали, включая ad hominem к предыдущему комментатору, опровергает утверждение что 6.5 — это ровно 6.5 и в десятичном виде и в двоичном?


          1. salas
            03.08.2019 16:29

            >>> type(x)
            <type 'float'>
            >>> print x
            6.5
            >>> print x==6.5
            False

            Вы всё ещё уверены, что 6.5 — это всегда 6.5?


            1. slonopotamus
              03.08.2019 16:45
              +1

              [redacted]

              В вашем примере не показано чему равен x.

              Есть IEEE 754 вполне недвусмысленно описывающая работу чисел с плавающей точкой. Процессоры ей следуют. В рамках IEEE 754
              13/2=6.5
              5+1.5=6.5

              Also, утверждение было про десятичную и двоичную системы счисления, а не про «как print в питоне неизвестно какой версии, скорее всего 2.х, печатает float'ы».


              1. salas
                05.08.2019 21:48

                Исходное утверждение было «6,5 — это всегда 6,5». У него есть два возможных уточнения:
                1) что там «под капотом» — ну да, IEEE754;
                2) что можно увидеть в логе, наступив на эти грабли.


                Я помнил эти грабли из питона, но, оказывается, их в третьем действительно убрали. Ну, есть же в вебе два других популярных языка, в которых старые грабли многочисленны и бережно сохраняемы — и один из них превзошёл мои ожидания:


                php > echo PHP_VERSION;
                7.3.8
                php > $x = ((6 + 0.1 * 3 + 0.2) - 6.2 + 0.2) * 13;
                php > echo '$x='.$x.', $x<6.5: '.($x<6.5 ? 'yes': 'no').', round($x)='.round($x);
                $x=6.5, $x<6.5: yes, round($x)=7


              1. Ketovdk
                05.08.2019 11:11

                чтобы восстановить это, можете сделать нечто в духе 6,5 — 0,0000000001 + 0,0000000001 или что-то такое. Идея в том, что вы никогда не знаете, как числа будут округляться в памяти, с учетом того, что помимо ограничений мантисы, в языках есть внутренние оптимизации.


                1. Pand5461
                  05.08.2019 15:08

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


                  1. Ketovdk
                    05.08.2019 15:13

                    отлично. Но вам не кажется, что в высокоуровневом коде на языке c# не стоит опираться на специальные флаги транслятора и правила стандарта, который знаете только вы?


                    1. Pand5461
                      05.08.2019 17:03

                      Я не понял, в чём именно вопрос.
                      а) Если дошло до такой задачи, где важны тонкости округления, то, может, стоит брать не C#, а Фортран?
                      или
                      б) С чего бы "высокоуровневым" языкам давать программисту возможность выбора, каким образом математические выражения в них транслируются в машинный код и какие оптимизации при этом могут применяться?


                      Моё мнение как раз в том, что в (б) — с того, чтобы вопрос (а) не возникал. Не особо убудет от разработчиков компилятора, если будет флаг, при включении которого x + 1e-10 - 3e-10 не будет автоматически оптимизироваться до x - 2e-10, а float + int + float + int не будет преобразовываться в (float + float) + (int + int) для параллелизации сложений. А кому-то контроль на этом уровне может внезапно оказаться нужен.


                      1. Ketovdk
                        05.08.2019 17:15

                        мне кажется, что вместо того, чтобы вдаваться в такие не очевидные подробности, можно просто писать round(x+0.0000001) и не париться


                        1. Pand5461
                          05.08.2019 18:33

                          А что делать, если x больше 109, и добавление к нему 10-7 его не меняет, т.к. это за гранью двойной точности?


                          Стандарты, спецификации и флаги — они для того и существуют, чтобы, когда оказалось, что париться таки надо, — поведение было подконтрольно программисту и документировано, а лучше стандартизовано.


      1. AnisimovAndrey
        02.08.2019 21:16
        +1

        Кнокретно 6.5 может и всегда 6.5, но вообще, это правило не всегда работает тыц


        1. slonopotamus
          03.08.2019 15:26
          +1

          Какое "это" правило?


        1. ainoneko
          03.08.2019 16:59

          Кнокретно 6.5 может и всегда 6.5

          Если это именно 6.5, а не почти оно:
          >>> ((6 + 0.1 * 3 + 0.2) - 6.2 + 0.2) * 13
          6.499999999999998
          >>> ((6 + 0.1 * 3 + 0.2) - 6) * 13
          6.5
          


          1. slonopotamus
            03.08.2019 17:14

            Если число не является 6.5, то оно не является 6.5. Логично, капитан.

            Обобщим: есть числа, представимые с конечной точностью в некоей системе счисления, а есть непредставимые. Компьютеры обычно используют двоичную систему счисления. Память компьютеров штука не бесконечная. Поэтому не все числа компьютер может хранить без потери точности.


  1. Pand5461
    02.08.2019 16:27

    Дело в том, что «банковское» округление работает чуть хитрее — оно округляет число до ближайшего четного целого числа, а не до ближайшего целого по модулю

    Это же только когда дробная часть в точности равна 1/2, и ближайших целых два равноудалённых.
    Вполне логично, кстати, особенно когда цифры сначала округляются до десятых, а потом решаешь, что точности до целых уже достаточно. "Школьный" способ — при дробной части 1/2 округлять с повышением модуля — даёт, что какое-нибудь 3.48 округляется сначала до 3.5, а потом до 4. "Банковский", конечно, тоже, но также есть вероятность, что рядом 6.54 округлилось сначала до 6.5, а затем до 6. То есть "в среднем", действительно, можно ожидать более "несдвинутое" (unbiased) округление.
    Математически, кстати, тоже логично: если двоичная дробная часть равна 0.12, то округляем не до 20, где есть неоднозначность, а до 21.


    1. LeX_KaR Автор
      02.08.2019 16:38

      Мне тут больше понравилось «случайное округление». Чисто интуитивно почему-то кажется, что оно будет давать более нормальное распределение, чем банковское… Хотя математики наверняка уже всё просчитали :)


      1. Pand5461
        02.08.2019 16:54
        +1

        Не нормальное, а равномерное :)


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


        1. LeX_KaR Автор
          02.08.2019 16:58

          Да, описался слегка :) А какого рода претензии могут возникнуть с функции, которая делает что-либо «якобы» случайно. Я к тому, что её случайность и есть желаемый результат работы, причем тут её «чистота» (независимости и т.д.)?


          1. mayorovp
            02.08.2019 17:00

            При том, что при использовании ГСЧ результат функции перестаёт быть воспроизводимым, нарушается условие ?x f(x) == f(x)


            1. LeX_KaR Автор
              02.08.2019 17:03

              Я скорее про то, как это может выкатиться боком на практике? Ибо получение не воспроизводимого рандомного результата «направления» округления и есть ожидаемый результат. Нарушение ?x f(x) == f(x) в данном случае умышленное.


              1. alexs0ff
                02.08.2019 17:07
                +2

                Я скорее про то, как это может выкатиться боком на практике?

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


              1. Ketovdk
                02.08.2019 17:16

                тесты упадут. Ну и в целом, получается не очень детерминировано


          1. Pand5461
            02.08.2019 17:22

            Никаких, при условии, что функция round() из стандартной библиотеки работает детерминированно.
            Иначе, как уже заметили, будет много радости при отладке и объяснении пользователям, почему одни и те же входные данные им дают разные ответы.


  1. jknight
    02.08.2019 16:46

    За 5 лет программирования на проде (признаю, немного, да, но опыт какой-то есть), ни разу не пришлось округлять числа. Гораздо полезнее были предсказуемые операции вроде floor/ceil. Товарищи, а кто вообще хоть раз округлением занимался, расскажите, зачем! Правда, интересно :)


    1. LeX_KaR Автор
      02.08.2019 16:55

      Ну а как же погрешности? floor/ceil дадут большую совокупную при округлении к целым


      1. jknight
        02.08.2019 17:08

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


      1. panteleymonov
        02.08.2019 17:40

        floor/ceil чаше применяются для метрических расчетов чем для бухгалтерских и теоретических. В частности схема
        i = floor(x);
        f = x - i;

        используется чаще в программировании.


    1. RouR
      02.08.2019 22:22

      На сумму начислить % и округлить до копеек.


  1. mayorovp
    02.08.2019 16:55
    +1

    Банки тут ни при чем, округление до чётного используется потому что оно более стабильно и не даёт "дрейфа" при сложении и умножении. Его ещё Кнут рекомендовал.


    Обычное округление:


    5 + 0.5 = 5.5 ? 6
    6 - 0.5 = 5.5 ? 6
    6 + 0.5 = 6.5 ? 7

    Округление до чётного:


    5 + 0.5 = 5.5 ? 6
    6 - 0.5 = 5.5 ? 6
    6 + 0.5 = 6.5 ? 6


    1. GamaleyVV
      02.08.2019 17:55

      Правильно. Банковское Округление — это Округление до ближайшего четного.


    1. qw1
      03.08.2019 12:04

      Если бы ещё кто пояснил, что такое «дрейф» и какой от него вред.
      А, продолжая ваш пример с «окрулением до чётного»,
      5 + 0.5 = 5.5 ? 6
      5 - 0.5 = 4.5 ? 4

      Никакой стабильности…


      1. mayorovp
        03.08.2019 12:32
        +1

        Дрейф — это когда выражение "x + y — y + y — y + y — y + ..." уходит довольно далеко от изначального числа x.


        1. qw1
          03.08.2019 15:22

          В этом выражении нет округлений. Если предположить округление после каждого действия, то вроде понятно, о чём речь.


  1. alexs0ff
    02.08.2019 16:59
    -1

    Гораздо интересней решения проблем, появляющихся при округлении.
    Например, классика:
    есть некоторые ежедневные значения.
    каждый месяц их складываем и округляем (например отбрасываем копейки любым методом — банковским /к четному целому).
    в конце года округляем сумму ежедневных значений за год.
    А теперь сравниваем с суммой за 12 месяцев — с большой вероятностью они будут разные.


    1. greg123
      02.08.2019 20:27

      А в чем, собственно, проблема? Вы же сами округлили числа. Округление это уменьшение точности и точность, на которую вы уменьшили число, зависит от числа. Вы уменьшили точность разных чисел и хотите, чтобы их сумма совпала?


      1. alexs0ff
        02.08.2019 21:06

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


        1. Cryvage
          03.08.2019 00:24

          Так это же классика. Погрешность обязательно становится проблемой, если она накапливается. Накопление погрешности — это то, чего следует избегать в первую очередь, когда дело касается каких-то расчётов.
          В описанном вами случае, очевидно, что округлять следует только конечный результат. Если бухгалтерам не нужна точность до тысячных долей, то их можно отбросить в каком-то отчёте, или документе. Но отчёт за более долгий период не должен быть суммой других отчётов, которые были округлены. Он должен считаться по исходным данным.


          1. alexs0ff
            03.08.2019 08:24

            Так это же классика.

            Так в стартовом сообщении я так и написал про классику.

            Он должен считаться по исходным данным.

            Да, так и делается. Но проблема в другом — у бухгалтеров есть уже распечатанные ежемесячные отчеты с некоторыми округленными суммами, так они хотят, чтобы они также сходились с годовым, как в программе (которая округляет исходные данные), так и при подсчете на калькуляторе округленных заранее ежемесячных сумм.


            1. mayorovp
              03.08.2019 12:44

              Наверняка тут не "бухгалтерия хочет", а очень даже "налоговая хочет". Поэтому тут надо не решать какие-то абстрактные проблемы округления, а в точности закодировать единственный правильный порядок вычислений, который должен сообщить бухгалтер. Так что это ни разу не интересная задача.


              1. alexs0ff
                03.08.2019 12:52

                «бухгалтерия хочет», а очень даже «налоговая хочет»

                Помимо налоговой, есть еще и контрагенты, поставщики и т.д.
                а в точности закодировать единственный правильный порядок вычислений

                И где такой взять бухгалтеру? Выше уже ответили, что с понижением точности, будет расти разность — это математика. Можно нагородить костылей, но на то они и костыли, что где-то можно не учесть/забыть и опять все развалится.
                При проверке банков у нас, я слышал, что ЦБ дает поле для маневра в несколько тысяч на дельту. И самый простой способ — это договориться, что несколько копеек/рублей вполне допустимая разница.


                1. mayorovp
                  03.08.2019 13:01

                  И где такой взять бухгалтеру?

                  Его должны были ему научить...


                  Выше уже ответили, что с понижением точности, будет расти разность — это математика.

                  А это не проблема. В бухгалтерии требуется не произвольная, а строго определенная точность.


  1. Nagg
    02.08.2019 20:59
    +1

    Недавно нашел забавный баг в Math(F).Round :D
    https://github.com/dotnet/coreclr/issues/25857


    1. slonopotamus
      03.08.2019 18:27

      Прелестно


  1. AnisimovAndrey
    02.08.2019 21:13

    Бывают случаи когда MidpointRounding.AwayFromZero не помогает :)
    Например, 150.515 внезапно округляется до 150.51, даже если указать MidpointRounding.AwayFromZero.
    Выход тут только преобразование в decimal, так как double точности не хватает и в памяти оно выглядит не просто как 150.515.
    С float ещё хуже.
    Зря в первый коммент минус кинули. Бывают сюрпризы.


    1. LeX_KaR Автор
      02.08.2019 22:24

      Ну да, если говорить об округлении не до целых, то всё немного сложнее


      1. ainoneko
        03.08.2019 17:12

        При округлении до целых это возникает про бОльших числах: попробуйте в Javascript получить число 9007199254740993 ?\_(?)_/?


  1. trolley813
    02.08.2019 22:41

    Почему бы не использовать старый добрый способ с (int)(x + 0.5 + 1e-6)? В реальных задачах почти не бывает кейсов, когда нужно округлять числа типа 6.4999999


  1. DrPass
    03.08.2019 01:18

    И вроде как незнание этой мелочи сильно не сказывается на вашей работе, но как только вам приходится работать со статистикой и расчетами показателей, базирующихся на куче всяких записей и циферок эта штука вылазит боком

    Я вот тут совсем не понял аргументацию автора. Как банковское округление может «вылезти боком» при работе со статистикой и расчетами показателей, базирующихся на куче всяких записей, если это как раз тот случай, где его необходимо использовать. Математическое округление при работе с крупными массивами данных приводит к некорректным итоговым суммам, и как раз для этого и было придумано банковское округление, которое дает статистически верный результат на массивах данных :)


  1. grafstroganov
    03.08.2019 08:01

    decimal — не есть тип с плавающей точкой. float и double — да, decimal — нет


    1. alexs0ff
      03.08.2019 08:29

      decimal — не есть тип с плавающей точкой

      Спецификация C# с Вами не согласна.
      docs.microsoft.com/ru-ru/dotnet/api/system.decimal
      Represents a decimal floating-point number.


  1. ivanbolhovitinov
    03.08.2019 08:44

    Для меня самая насущная проблема в округлении — это получение корректных бухгалтерских отчетов в «тысячах» рублей.
    Для примера: есть три операции и итог: 3 333.33 + 3 333.34 + 3 333.33 = 10 000.00
    Делим на тысячу, округляем, получаем: 3.3 + 3.3 + 3.3 = 10.0. Как-то глупо.
    Пробуем еще: 3.3 + 3.3 + 3.3 = 9.9. Тоже глупо, потому что отчет проверяется через сальдо счета 10 000.00, а отчет показывает 9.9.

    Значит надо добиться 3.4 + 3.3 + 3.3 = 10.0.
    Таких функций округления уже нет.
    Такое огругление пока делаю только руками в каких-то простых случаях. Считаю разницу, и добавляю её в первую/последнюю строку.

    Или даже в этом простом подходе есть свои дырки, например если очень много мелких сумм, то разница целого отчета может дорасти до рублей и добавлять её в случайную строку — тоже глупо, потому что сумма отдельной строки может измениться до не узнаваемости. Значит надо размазывать её равномерно среди всех строк.

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

    Есть какие-то универсальные подходы?


    1. caballero
      03.08.2019 11:25
      -2

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


  1. Androniy
    03.08.2019 11:34

    В 1998ом году когда была деноминация необходимо было конвертировать базу данных бухгалтерских операций в новый формат. При этом чтобы итоговые суммы сошлись до копейки. Ни один из способов округления не позволял этого сделать, т.к. операций было очень много на мелкие суммы, и в итоге набегало несколько копеек разницы со старыми итогами (при округлении терялся один десятичный разряд). Пришлось писать свое округление, считать разницу в итоговой сумме и случайным образом добавлять по одной копейке до тех пор, пока разница с итогом не исчезнет…


  1. Exponent
    03.08.2019 14:31
    +1

    Тоже столкнулся с этой особенностью .NET. Делал отчеты и данные брал из SQL Server, а он использует арифметичное округление, а .NET банковское. Часть данных округлялась в базе, другая часть округлялась в коде C#. В итоге результаты отчетов различались в 100 000 евро. Пришлось объяснять финансистам откуда берутся эти 100 000 евро, они естественно даже не слышали что есть другой метод округления. Было неприятно.


  1. Almet
    05.08.2019 11:39

    Отлично, спасибо большое за информацию, новичкам пригодится


  1. DieSlogan
    05.08.2019 11:57

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

    var number = 6.9;
    var intNumber = (int)number;

    В этом примере я привожу тип с плавающей запятой (double в данном случае) к целочисленному int. Так вот, при приведении типов к целочисленному вся не целая часть просто отсекается.


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

    public static int ToInt32(float inputValue)
    { return (int)(inputValue * 100); }