Как проверить, есть ли остаток от деления чисел в С++?
Как получить остаток от операции целочисленного деления?
С математической точки зрения, операция вычисления остатка в C/С++ для отрицательных чисел, например -11 % 8 == -3 , выдаёт не тот результат к которому мы привыкли со школьной скамьи. Конечно, это вопрос договорённости, но. Эта проблема иногда вызывает недоумение и горячие споры.
Если задача определить лишь отсутствие остатка, тогда достаточно % . Однако, есть частное решение когда делитель — это число степени 2. Этот же способ является наиболее простым для определения остатка от деления на числа больше 1 равные степени 2.
Почему это важно? Рассмотрим последовательность mod(n, 4)
Для n&(4-1) последовательность периодическая
Для n%4 последовательность, вообще говоря, непериодическая
Работать с периодической последовательностью гораздо приятней там где требуется соблюдать равные интервалы между событиями. Можно соорудить конструкцию типа
и быть уверенным, что каждое событие будет возникать с завидной регулярностью независимо от знака i и дополнительных case не потребуется.
Как найти остаток от деления в c
В C# используется большинство операций, которые применяются и в других языках программирования. Операции представляют определенные действия над операндами — участниками операции. В качестве операнда может выступать переменной или какое-либо значение (например, число). Операции бывают унарными (выполняются над одним операндом), бинарными — над двумя операндами и тернарными — выполняются над тремя операндами. Рассмотрим все виды операций.
Бинарные арифметические операции:
Операция сложения двух чисел:
Операция вычитания двух чисел:
Операция умножения двух чисел:
операция деления двух чисел:
При делении стоит учитывать, что если оба операнда представляют целые числа, то результат также будет округляться до целого числа:
Несмотря на то, что результат операции в итоге помещается в переменную типа double, которая позволяет сохранить дробную часть, но в самой операции участвуют два литерала, которые по умолчанию рассматриваются как объекты int, то есть целые числа, и результат то же будет целочисленный.
Для выхода из этой ситуации необходимо определять литералы или переменные, участвующие в операции, именно как типы double или float:
Операция получение остатка от целочисленного деления двух чисел:
Также есть ряд унарных операций, в которых принимает участие один операнд:
Инкремент бывает префиксным: ++x — сначала значение переменной x увеличивается на 1, а потом ее значение возвращается в качестве результата операции.
И также существует постфиксный инкремент: x++ — сначала значение переменной x возвращается в качестве результата операции, а затем к нему прибавляется 1.
Операция декремента или уменьшения значения на единицу. Также существует префиксная форма декремента ( —x ) и постфиксная ( x— ).
При выполнении сразу нескольких арифметических операций следует учитывать порядок их выполнения. Приоритет операций от наивысшего к низшему:
Умножение, деление, получение остатка
Для изменения порядка следования операций применяются скобки.
Рассмотрим набор операций:
Здесь мы имеем дело с тремя операциями: декремент, вычитание и умножение. Сначала выполняется декремент переменной c, затем умножение b*a, и в конце вычитание. То есть фактически набор операций выглядел так:
Но с помощью скобок мы могли бы изменить порядок операций, например, следующим образом:
Ассоциативность операторов
Как выше было отмечено, операции умножения и деления имеют один и тот же приоритет, но какой тогда результат будет в выражении:
Стоит нам трактовать это выражение как (10 / 5) * 2 или как 10 / (5 * 2) ? Ведь в зависимости от трактовки мы получим разные результаты.
Когда операции имеют один и тот же приоритет, порядок вычисления определяется ассоциативностью операторов. В зависимости от ассоциативности есть два типа операторов:
Левоассоциативные операторы, которые выполняются слева направо
Правоассоциативные операторы, которые выполняются справа налево
Все арифметические операторы являются левоассоциативными, то есть выполняются слева направо. Поэтому выражение 10 / 5 * 2 необходимо трактовать как (10 / 5) * 2 , то есть результатом будет 4.
Арифметические операторы C#
При изучении любого языка программирования, будь то Delphi, Python или C#, одной из основных тем изучения на начальном этапе является перечень основных операторов в языке и их применение. Конечно, со временем работать с теми или иными операторами начинаешь, как говориться, «на автомате», но в самом начале пути изучения нового языка эта тема является безусловно одной из основных. Все возможные операторы я, конечно, сейчас рассматривать не буду, но основные всё же стоит рассмотреть, чтобы в дальнейшем не путаться в их применении.
Арифметические операторы C#
Как и в Delphi операторы делятся на две группы: унарные и бинарные.
К унарным арифметическим операторам в C# относят:
- ++ (приращение),
- — (уменьшение),
- + (плюс) и — (минус)
- * (умножение),
- / (деление),
- % (остаток от деления),
- + (сложение) и — (вычитание).
Большинство из этих операторов нам может быть известно из Delphi (кто не знает, как работает умножение или деление в Delphi?), но некоторые из операторов имеют свои особенности в C#. Например, в статье про работу с числами в C# упоминалось что деление 1/3 в C# не вернет нам результат 0,3333 Начнем с унарных.
Унарные арифметические операторы C#
Операторы приращения (++) и уменьшения (—)
Ближайшими аналогами в Delphi для этих операторов являются процедуры Inc() и Dec() .
Оператор инкремента ++ увеличивает операнд на 1, а оператор декремента — , соответственно, уменьшает операнд на 1. При этом операндом должна быть переменная , свойство или индексатор.
Например, результатом выполнения вот такого кода:
Вроде бы все вполне себе логично — одну переменную увеличили на 1, вторую — уменьшили на 1 и вывели результат в виде строки.
«Усложним» пример и напишем следующее:
Казалось бы, результат измениться не должен и строка должна быть той же, но нет. В результате мы получим:
То есть, переменные а и b получили первоначальные значения f и i . Почему так происходит?
Дело в том, что операторы инкремента и декремента в C# поддерживается в двух формах: постфиксной (например, x++ ) и префиксной (например, ++x ).
Результатом постфиксного оператора приращения или уменьшения является значение переменной перед выполнением операции, а результатом префиксного оператора, соответственно, значение переменно после выполнения операции.
Теперь, если взглянуть на пример выше, становится понятно поведение программы — мы использовали постфиксные операторы и в результате, вначале переменной a присваивается значение f , а уже потом значение переменной f увеличивается на 1. Аналогично происходит и с переменной b — она получает первоначальное значение i и только после этого i уменьшается на 1.
На первый взгляд может показаться странным, но вот такое действие:
не поменяет значение переменной i — она также останется равной нулю. Причина такого поведения C# описана выше — использовалась постфиксная форма инкремента.
Чтобы наше переменные получили измененные значения код надо переписать с использованием префиксной формы операторов, то есть вот так:
Результатом выполнения будет строка:
Унарные операторы плюса и минуса
Как и в Delphi, унарный оператор + возвращает значение полученного операнда. Унарный оператор — изменяет знак операнда на противоположный. Здесь никаких «подводных камней» нет и результат выполнения следующего кода можно угадать с первого раза:
Результатом будет строка:
Остается только отметить, что целочисленный тип ulong не поддерживает унарный минус. Если мы попытаемся сделать вот так:
То ещё до компиляции получим ошибку:
При этом для целочисленных типов ushort и uint оператор «-» работает. Вот такой код:
Выведет на экран следующее:
Бинарные арифметические операторы
Умножение (*)
Оператор умножения * вычисляет произведение операндов, например:
вернет значение 50 , а типом данных для произведения будет, ожидаемо, int . Соответственно, в этом примере:
Программа вернет нам вещественный тип float (System.Single). Другое дело — оператор деления.
Деление (/)
В C# различают деление целых чисел и деление чисел с плавающей точкой (запятой).
Для операндов целочисленных типов ( int , short , uint и т.д.) результат оператора / является целочисленным типом, который равен частному двух операндов, округленному в сторону нуля. Собственно об этом мы говорили в заметке про работу с числами в C#.
Для типов float , double и decimal результатом оператора / является частное двух операндов. Однако и здесь есть момент, который стоит помнить, а именно: если один из операндов — это decimal , второй операнд не может быть ни float , ни double , так как ни float , ни double не преобразуется неявно в тип decimal . Необходимо явным образом преобразовать операнд float или double в тип decimal (также, про такое поведение C# см. здесь). Вот такой код:
Приведет к ошибке:
Чтобы код сработал без ошибок надо произвести приведение типов, а именно переписать код следующим образом:
и деление пройдет успешно с результатом
Остаток от деления (%)
Ближайшим «родственником» в Delphi является оператор mod . Но и здесь у C# есть свои особенности.
Для целочисленных операндов результатом a % b является значение полученное из выражения a — (a / b) * b . Знак ненулевого остатка такой же, как и у левого операнда, например:
вернет нам значение 1. Тут всё ровно так же, как и в Delphi. В отличие от Delphi, в C# можно получить остаток от деления вещественных чисел, например:
В Delphi с оператором mod такое проделать нельзя. Для получения аналогичного результата нам бы пришлось воспользоваться функцией FMod() из модуля Math .
Операторы сложения (+) и вычитания (-)
Эти операторы так же, как и везде производят математическое сложение/вычитание правого операнда из левого. Никаких подводных камней и особенностей в этом плане в C# нет.
Операторы составного присваивания
В C# имеются также операторы составного присваивания (чего нет в Delphi), которые можно использовать, например, для более короткой записи арифметических операций и в общем случае выглядят следующим образом:
где op — какой-либо из рассмотренных выше операторов. Читается такое выражение в развернутом виде следующим образом:
вернет нам значения, которые написаны в комментариях к коду, то есть строки:
Из-за восходящих приведений результат операции op может быть невозможно неявно преобразовать в тип T из x. В этом случае, если op является предопределенным оператором, и результат операции является явно преобразуемым в тип T«x, выражение составного присваивания формы x op= y эквивалентно x = (T)(x op y), за исключением того, что x вычисляется только один раз.
Пример такого поведения представлен там же в справке:
Мы бы, возможно, ожидали, что последний вывод в консоль содержал бы число 300 , но тип byte у нас только от 0 до 255. В результате в вывод попало значение ( 300-256 = 44 ).
Приоритет и ассоциативность операторов
Арифметические операторы выполняются в следующем порядке (по убыванию приоритета):
- Постфиксный инкремент x++ и декремент x—
- Префиксный инкремент ++x и декремент —x , унарные операторы + и —
- Мультипликативные операторы * , / , и %
- Аддитивные операторы + и —
Бинарные арифметические операторы имеют левую ассоциативность. То есть операторы с одинаковым приоритетом вычисляются в направлении слева направо.
Порядок вычисления, определяемый приоритетом и ассоциативностью операторов, можно изменить с помощью скобок ( () ).
Итого
Сегодня мы узнали, какие виды арифметических операторов есть в C#, как они работают и в чем их особенности. А также разобрали несколько примеров, показывающих особенности вычисления арифметических выражений в C#.
Урок №39. Арифметические операторы
На этом уроке мы рассмотрим арифметические операторы и их использование в языке С++.
Унарные арифметические операторы
Унарные операторы — это операторы, которые применяются только к одному операнду. Существуют два унарных арифметических оператора: плюс ( + ) и минус ( − ).
| Оператор | Символ | Пример | Операция |
| Унарный плюс | + | +x | Значение x |
| Унарный минус | − | −x | Отрицательное значение x |
Унарный оператор + возвращает значение операнда. Другими словами, +5 = 5 или +х = х . Унарный плюс вам, скорее всего, не придется использовать. Его по большей части добавили в качестве симметрии с унарным оператором минус. Унарный оператор минус возвращает операнд, умноженный на −1 . Например, если х = 5 , то −х = −5 .
Оба этих оператора пишутся непосредственно перед самим операндом, без пробела ( −x , а не − x ).
Не следует путать унарный оператор минус с бинарным оператором вычитания, хоть они и используют один и тот же символ. Например, в выражении х = 5 − −3; , первый минус — это оператор вычитания, а второй — унарный минус.
Бинарные арифметические операторы
Бинарные операторы — это операторы, которые применяются к двум операндам (слева и справа). Существует 5 бинарных операторов.
| Оператор | Символ | Пример | Операция |
| Сложение | + | x + y | x плюс y |
| Вычитание | − | x − y | x минус y |
| Умножение | * | x * y | x умножить на y |
| Деление | / | x / y | x разделить на y |
| Деление с остатком | % | x % y | Остаток от деления x на y |
Операторы сложения, вычитания и умножения работают так же, как и в обычной математике. А вот деление и деление с остатком рассмотрим детально.
Деление целых чисел и чисел типа с плавающей точкой
Оператор деления имеет два режима. Если оба операнда являются целыми числами, то оператор выполняет целочисленное деление. Т.е. любая дробь (больше/меньше) отбрасывается и возвращается целое значение без остатка, например, 7 / 4 = 1 .
Если один или оба операнда типа с плавающей точкой, то тогда будет выполняться деление типа с плавающей точкой. Здесь уже дробь присутствует. Например, выражения 7.0 / 3 = 2.333 , 7 / 3.0 = 2.333 или 7.0 / 3.0 = 2.333 имеют один и тот же результат.
Попытки деления на 0 (или на 0.0 ) станут причиной сбоя в вашей программе, и это правило не следует забывать!
Использование оператора static_cast в операциях деления
На уроке о символьном типе данных char мы уже использовали оператор static_cast для вывода ASCII-символов в виде целых чисел.
Аналогичным образом мы можем использовать static_cast для конвертации целого числа в число типа с плавающей точкой. Таким образом, вместо целочисленного деления выполнится деление типа с плавающей точкой. Например:
Результат выполнения программы:
int / int = 1
double / int = 1.75
int / double = 1.75
double / double = 1.75
Деление с остатком
Оператор деления с остатком ( % ) работает только с целочисленными операндами и возвращает остаток от целочисленного деления. Например:
Пример №1: 7 / 4 = 1 с остатком 3 , таким образом, 7 % 4 = 3 .
Пример №2: 25 / 7 = 3 с остатком 4 , таким образом, 25 % 7 = 4 . Остаток составляет не дробь, а целое число.
Пример №3: 36 % 5 = 7 с остатком 1 . В числе 36 только 35 делится на 5 без остатка, поэтому 36 − 35 = 1 , 1 — это остаток и результат.
Данный оператор чаще всего используют для проверки деления без остатка одних чисел на другие. Если х % у == 0 , то х делится на у без остатка.
Например, мы хотим написать программу, которая выводит числа от 1 до 100 по 20 значений в каждой строке. Мы можем использовать оператор деления с остатком для создания разрыва строк. Несмотря на то, что мы еще не рассматривали цикл while, в следующей программе всё максимально просто и понятно:
Результат выполнения программы:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
О while мы еще поговорим на соответствующем уроке.
Отрицательные числа в операциях деления до C++11
До C++11, если любой из операндов целочисленного деления является отрицательным, то компилятор округляет результат самостоятельно! Например, результатом −5 / 2 может быть либо −3 , либо −2 . Однако большинство современных компиляторов округляют числа в сторону нуля (например, в −5 / 2 результатом будет −2 ). В спецификации C++11 определили, что компилятор должен всегда округлять к нулю (или, проще говоря, просто отбрасывать дробь).
Также до C++11, если один из операндов оператора деления с остатком является отрицательным, то результат может быть как положительным, так и отрицательным! Например, результатом −5 % 2 может быть как 1 , так и −1 . В спецификации C++11 решили сделать так, чтобы результат a % b был того же знака, что и значение а .
Арифметические операторы присваивания
| Оператор | Символ | Пример | Операция |
| Присваивание | = | x = y | Присваиваем значение y переменной x |
| Сложение с присваиванием | += | x += y | Добавляем y к x |
| Вычитание с присваиванием | −= | x −= y | Вычитаем y из x |
| Умножение с присваиванием | *= | x *= y | Умножаем x на y |
| Деление с присваиванием | /= | x /= y | Делим x на y |
| Деление с остатком и с присваиванием | %= | x %= y | Присваиваем остаток от деления x на y переменной x |
До этого момента, когда нам нужно было добавить число 5 к определенной переменной, мы делали следующее: