En un programa en C, estaba probando las siguientes operaciones (solo para verificar el comportamiento)
x = 5 % (-3);
y = (-5) % (3);
z = (-5) % (-3);
printf("%d ,%d ,%d", x, y, z);
me dio salida como (2, -2 , -2)
en gcc. Esperaba un resultado positivo cada vez. ¿Puede un módulo ser negativo? ¿Alguien puede explicar este comportamiento?
Respuestas:
C99 requiere que cuando
a/b
sea representable:(a/b) * b
+a%b
será iguala
Esto tiene sentido, lógicamente. ¿Correcto?
Veamos a qué conduce esto:
El ejemplo A.
5/(-3)
es-1
=>
(-1) * (-3)
+5%(-3)
=5
Esto solo puede suceder si
5%(-3)
es 2.El ejemplo B.
(-5)/3
es-1
=>
(-1) * 3
+(-5)%3
=-5
Esto solo puede suceder si
(-5)%3
es-2
fuente
-5/3
es-2
y el mod se convierte en 1. En resumen: un módulo tiene un signo que sigue al signo de dividendo (truncado), el otro módulo tiene un signo que sigue al signo divisor (Knuth).El
%
operador en C no es el operador de módulo sino el operador restante .Los operadores de módulo y resto difieren con respecto a los valores negativos.
Con un operador restante, el signo del resultado es el mismo que el del dividendo, mientras que con un operador de módulo el signo del resultado es el mismo que el divisor.
C define la
%
operación paraa % b
como:con
/
la división entera con truncamiento hacia0
. Ese es el truncamiento que se hace hacia0
(y no hacia la inifinidad negativa) que define el%
como un operador restante en lugar de un operador de módulo.fuente
remainder
ymodulo
en Schemerem
ymod
en Haskell). Las especificaciones de estos operadores difieren en estos lenguajes en cómo se realiza la división: truncamiento hacia 0 o hacia infinito negativo. Por cierto estándar de C nunca llama a la%
del operador de módulo , sólo que nombre que el operador% .remainder
función en C, que implementa el resto IEEE con la semántica de redondeo hacia el más cercano en la divisiónBasado en la especificación C99:
a == (a / b) * b + a % b
¡Podemos escribir una función para calcular
(a % b) == a - (a / b) * b
!Para la operación de módulo, podemos tener la siguiente función (suponiendo
b > 0
)Mi conclusión es que
a % b
en C es una operación restante y NO una operación de módulo.fuente
b
es negativo (y de hecho parar
yb
ambos negativos que da resultados menos-b
). Para garantizar resultados positivos para todas las entradas que pueda usarr + abs(b)
o para que coincidan conb
el signo, puede cambiar la condición ar*b < 0
.r + abs(b)
es UB cuandob == INT_MIN
.No creo que haya necesidad de verificar si el número es negativo.
Una función simple para encontrar el módulo positivo sería esta:
Editar: Suponiendo
N > 0
yN + N - 1 <= INT_MAX
Esto funcionará tanto para valores positivos como negativos de x.
PS original: también según lo indicado por @chux, si su x y N pueden alcanzar algo como INT_MAX-1 e INT_MAX respectivamente, simplemente reemplace
int
conlong long int
.Y si también están cruzando límites de largo largo (es decir, cerca de LLONG_MAX), entonces manejará los casos positivos y negativos por separado como se describe en otras respuestas aquí.
fuente
N < 0
, el resultado puede ser negativo como enmodulo(7, -3) --> -2
. Tambiénx % N + N
puede desbordar lasint
matemáticas, que es un comportamiento indefinido. por ejemplo,modulo(INT_MAX - 1,INT_MAX)
podría resultar en -3.long long int
o manejar el caso negativo por separado (a costa de perder simplicidad).Las otras respuestas han explicado en C99 o posterior, la división de enteros que implican operandos negativos siempre se trunca hacia cero .
Tenga en cuenta que, en C89 , si el resultado redondeado hacia arriba o hacia abajo está definido por la implementación. Debido a que
(a/b) * b + a%b
es iguala
en todos los estándares, el resultado de%
involucrar operandos negativos también se define en la implementación en C89.fuente
%
puede ser negativo ya que es el operador restante , el resto después de la división, no después de Euclidean_division . Desde C99 el resultado puede ser 0, negativo o positivo.El módulo OP deseado es un módulo euclidiano clásico , no
%
.Para realizar un módulo euclidiano que esté bien definido cada vez que
a/b
se define,a,b
son de cualquier signo y el resultado nunca es negativo:fuente
El resultado de la operación del módulo depende del signo del numerador y, por lo tanto, obtiene -2 para y y z
Aquí está la referencia
http://www.chemie.fu-berlin.de/chemnet/use/info/libc/libc_14.html
fuente
En Matemáticas, de donde provienen estas convenciones, no hay afirmación de que la aritmética de módulo deba producir un resultado positivo.
P.ej.
1 mod 5 = 1, pero también puede ser igual a -4. Es decir, 1/5 produce un resto 1 de 0 o -4 de 5. (Ambos factores de 5)
Del mismo modo, -1 mod 5 = -1, pero también puede ser igual a 4. Es decir, -1/5 produce un resto -1 de 0 o 4 de -5. (Ambos factores de 5)
Para leer más, mire las clases de equivalencia en matemáticas.
fuente
a
yb
,b <> 0
. Según el teorema de la división euclidiana, existe exactamente un par de enterosm
,r
dondea = m * b + r
y0 <= r < abs( b )
. Lo dichor
es el resultado de la operación del módulo (matemático) y, por definición, no es negativo. Más lecturas y más enlaces en Wikipedia: en.wikipedia.org/wiki/Euclidean_division1 mod 5
siempre es 1. también-4 mod 5
puede ser 1, pero son cosas diferentes.De acuerdo con el estándar C99 , sección 6.5.5 Operadores multiplicativos , se requiere lo siguiente:
Conclusión
El signo del resultado de una operación restante, según C99, es el mismo que el del dividendo.
Veamos algunos ejemplos (
dividend / divisor
):Cuando solo el dividendo es negativo
Cuando solo el divisor es negativo
Cuando tanto el divisor como el dividendo son negativos
fuente
El operador de módulo da el resto. El operador de módulo en c generalmente toma el signo del numerador
Además, el operador de módulo (resto) solo se puede usar con tipo entero y no se puede usar con coma flotante.
fuente
Creo que es más útil pensar
mod
que se define en aritmética abstracta; no como una operación, sino como una clase de aritmética completamente diferente, con diferentes elementos y diferentes operadores. Eso significa que la sumamod 3
no es lo mismo que la suma "normal"; es decir; suma entera.Entonces cuando lo haces:
Está intentando asignar el número entero 5 a un elemento en el conjunto de
mod -3
. Estos son los elementos demod -3
:Entonces:
Digamos que tiene que permanecer despierto por alguna razón 30 horas, ¿cuántas horas le quedan de ese día?
30 mod -24
.Pero lo que C implementa no es
mod
, es un resto. De todos modos, el punto es que tiene sentido devolver negativos.fuente