Подтвердить что ты не робот

C, Python - различное поведение операции modulo (%)

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

В Python:

-1 % 10

создает 9

В C он производит -1!

1) Какой из них является правильным по модулю?
2) Как сделать работу mod в C такой же, как в Python?

4b9b3361

Ответ 1

  • Оба варианта правильны, однако в математике (в частности, в теории чисел) наиболее часто используется Python modulo.
  • В C вы выполняете ((n % M) + M) % M, чтобы получить тот же результат, что и в Python. E. g. ((-1 % 10) + 10) % 10. Обратите внимание, как это работает для целых положительных чисел: ((17 % 10) + 10) % 10 == 17 % 10, а также для обоих вариантов реализаций C (положительный или отрицательный остаток).

Ответ 2

Python имеет "истинную" модульную операцию, а C имеет операцию остатка.

Он имеет прямое отношение к тому, как обрабатывается отрицательное целочисленное деление, то есть округляется до 0 или минус бесконечно. Python округляется до минуса бесконечности и C (99) в направлении 0, но на обоих языках (n/m)*m + n%m == n, поэтому оператор% должен компенсировать в правильном направлении.

Ada более явна и имеет оба значения: mod и rem.

Ответ 3

В C89/90 поведение оператора деления и оператора остатка с отрицательными операндами определяется реализацией, что означает, что в зависимости от реализации вы можете получить либо поведение. Просто требуется, чтобы операторы соглашались друг с другом: из a / b = q и a % b = r следует a = b * q + r. Используйте статические утверждения в своем коде, чтобы проверить поведение, если оно критически относится к результату.

В C99 поведение, которое вы наблюдаете, стало стандартным.

Фактически, либо поведение имеет определенную логику в нем. Поведение Python реализует истинную модульную операцию. Поведение, которое вы наблюдаете, соответствует C, округляя до 0 (это также поведение Fortran).

Одна из причин, когда округление к 0 является предпочтительным в C, состоит в том, что вполне естественно ожидать, что результат -a / b будет таким же, как -(a / b). В случае истинного по модулю поведения -1 % 10 будет оцениваться до 9, что означает, что -1 / 10 должно быть -1. Это можно считать довольно неестественным, так как -(1 / 10) равно 0.

Ответ 4

Оба ответа верны, так как -1 modulo 10 совпадает с 9 modulo 10.

r = (a mod m)
a = n*q + r

Вы можете быть уверены, что |r| < |n|, но не значение r. Есть два ответа, отрицательные и положительные.


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

Если вы хотите получить положительный ответ, вы можете просто добавить 10, если найдете свой ответ отрицательным.

Чтобы заставить modulo работать одинаково на всех языках, просто помните, что:

n mod M == (n + M) mod M

и вообще:

n mod M == (n + X * M) mod M

Ответ 5

С помощью python 3.7 вы также можете использовать .remainder() из math встроенный модуль.

Python 3.7.0a0 (heads/master:f34c685020, May  8 2017, 15:35:30)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import math
>>> math.remainder(-1, 10)
-1.0

От docs:

Верните остаток в стиле IEEE 754 по x относительно y. Для конечного x и конечного ненулевого y это разность x - n*y, где n является ближайшим целым к точному значению частного x / y. Если x / y находится на полпути между двумя последовательными целыми числами, то для n используется ближайшее четное целое число. Остаток r = remainder(x, y), таким образом, всегда удовлетворяет abs(r) <= 0.5 * abs(y).

Особые случаи следуют за IEEE 754: в частности, remainder(x, math.inf) есть x для любого конечного x, а remainder(x, 0) и remainder(math.inf, x) повышают ValueError для любого не-NaN x. Если результат операции останова равен нулю, этот ноль будет иметь тот же знак, что и x.

На платформах с использованием двоичной с плавающей запятой IEEE 754 результат этой операции всегда точно представлен: ошибка округления не вводится.