La operación de módulo en números negativos en Python

Respuestas:

137

A diferencia de C o C ++, el operador de módulo de Python ( %) siempre devuelve un número que tiene el mismo signo que el denominador (divisor). Tu expresión da 3 porque

(-5) / 4 = -1.25 -> piso (-1.25) = -2

(-5)% 4 = (-2 × 4 + 3)% 4 = 3.

Se elige sobre el comportamiento C porque un resultado no negativo suele ser más útil. Un ejemplo es calcular los días de la semana. Si hoy es martes (día # 2), ¿cuál es el día de la semana N días antes? En Python podemos calcular con

return (2 - N) % 7

pero en C, si N ≥ 3, obtenemos un número negativo que es un número inválido, y necesitamos arreglarlo manualmente agregando 7:

int result = (2 - N) % 7;
return result < 0 ? result + 7 : result;

(Consulte http://en.wikipedia.org/wiki/Modulo_operator para ver cómo se determina el signo del resultado para diferentes idiomas).

Kennytm
fuente
6
Sorprendentemente, el operador de módulo de Python (%) no siempre devuelve un número con el mismo signo que el denominador (divisor). Ver stackoverflow.com/questions/48347515/…
zezollo
33

Aquí hay una explicación de Guido van Rossum:

http://python-history.blogspot.com/2010/08/why-pythons-integer-division-floors.html

Esencialmente, es para que a / b = q con el resto r conserve las relaciones b * q + r = a y 0 <= r <b.

Kevin
fuente
4
Los lenguajes como C ++ y Java también conservan la primera relación, pero ceden para los pisos negativos a, positivos b, mientras que Python. Siempre es cierto eso abs(r) < b, y ceil iff r <= 0.
Evgeni Sergeev
9

No existe una mejor manera de manejar la división de enteros y las modificaciones con números negativos. Sería bueno si a/bfuera de la misma magnitud y signo opuesto de (-a)/b. Sería bueno si a % bfuera un módulo b. Como realmente queremos a == (a/b)*b + a%b, los dos primeros son incompatibles.

Cuál mantener es una pregunta difícil y hay argumentos para ambas partes. C y C ++ redondean la división entera hacia cero (entonces a/b == -((-a)/b)), y aparentemente Python no lo hace.

David Thornley
fuente
1
"Sería bueno si a / b tuviera la misma magnitud y el signo opuesto de (-a) / b". ¿Por qué sería bueno? ¿Cuándo es ese un comportamiento deseado?
user76284
Porque entonces actuaría de la misma manera que la división y la multiplicación regulares y, por lo tanto, es intuitivamente fácil de trabajar. Sin embargo, eso puede no tener sentido matemáticamente.
Demis
6

Como se señaló, Python modulo hace una excepción bien razonada a las convenciones de otros lenguajes.

Esto le da a los números negativos un comportamiento uniforme, especialmente cuando se usa en combinación con el //operador de división de enteros, como %suele ser el módulo (como en matemáticas. Divmod ):

for n in range(-8,8):
    print n, n//4, n%4

Produce:

 -8 -2 0
 -7 -2 1
 -6 -2 2
 -5 -2 3

 -4 -1 0
 -3 -1 1
 -2 -1 2
 -1 -1 3

  0  0 0
  1  0 1
  2  0 2
  3  0 3

  4  1 0
  5  1 1
  6  1 2
  7  1 3
  • Python %siempre genera cero o positivo *
  • Python //siempre se redondea hacia el infinito negativo

* ... siempre que el operando derecho sea positivo. Por otra parte11 % -10 == -9

Bob Stein
fuente
Gracias, tu ejemplo me hizo entenderlo :)
Lamis
5

En Python , el operador de módulo funciona así.

>>> mod = n - math.floor(n/base) * base

entonces el resultado es (para su caso):

mod = -5 - floor(-1.25) * 4
mod = -5 - (-2*4)
mod = 3

mientras que otros lenguajes como C, JAVA, JavaScript usan truncamiento en lugar de floor.

>>> mod = n - int(n/base) * base

lo que resulta en:

mod = -5 - int(-1.25) * 4
mod = -5 - (-1*4)
mod = -1

Si necesita más información sobre el redondeo en Python, lea esto .

Munkhbold Enkhtur
fuente
3

Módulo, clases de equivalencia para 4:

  • 0: 0, 4, 8, 12 ... y -4, -8, -12 ...
  • 1: 1, 5, 9, 13 ... y -3, -7, -11 ...
  • 2: 2, 6, 10 ... y -2, -6, -10 ...
  • 3: 3, 7, 11 ... y -1, -5, -9 ...

Aquí hay un enlace al comportamiento de módulo con números negativos . (Sí, lo busqué en Google)

Wheaties
fuente
@NullUserException - sí, lo fue. fijo. Gracias.
Wheaties
1

También pensé que era un comportamiento extraño de Python. Resulta que no estaba resolviendo bien la división (en papel); Le estaba dando un valor de 0 al cociente y un valor de -5 al resto. Terrible ... Olvidé la representación geométrica de los números enteros. Al recordar la geometría de los enteros dada por la recta numérica, uno puede obtener los valores correctos para el cociente y el resto, y verificar que el comportamiento de Python sea correcto. (Aunque supongo que ya hace tiempo que resolvió su inquietud).

joser
fuente
1

También vale la pena mencionar que también la división en python es diferente de C: considere

>>> x = -10
>>> y = 37

en C esperas el resultado

0

¿Qué es x / y en Python?

>>> print x/y
-1

y% es módulo, ¡no el resto! Mientras que x% y en C produce

-10

Python rinde.

>>> print x%y
27

Puede obtener ambos como en C

La división:

>>> from math import trunc
>>> d = trunc(float(x)/y)
>>> print d
0

Y el resto (usando la división de arriba):

>>> r = x - d*y
>>> print r
-10

Este cálculo tal vez no sea el más rápido, pero funciona para cualquier combinación de signos de xey para lograr los mismos resultados que en C y además evita las declaraciones condicionales.

bebbo
fuente