Operación de módulo con números negativos

8 minutos de lectura

avatar de usuario
Alba

En un programa 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?

  • Posible duplicado de stackoverflow.com/questions/4003232/…

    – James

    30 de julio de 2012 a las 11:41

  • posible duplicado del operador Modulo con valores negativos

    – sugavaneshb

    8 de febrero de 2015 a las 8:46

  • Hay dos interpretaciones diferentes del módulo. torstencurdt.com/tech/posts/modulo-of-negative-numbers

    – tcurdt

    1 de febrero a las 12:26

avatar de usuario
ouah

Él % El operador en C no es el módulo operador pero el recordatorio operador.

Los operadores de módulo y resto difieren con respecto a los valores negativos.

Con un operador de resto, el signo del resultado es el mismo que el del dividendo (numerador), mientras que con un operador de módulo el signo del resultado es el mismo que el del divisor (denominador).

C define el % operación para a % b como:

  a == (a / b * b) + a % b

con / la división entera con truncamiento hacia 0. Ese es el truncamiento que se hace hacia 0 (y no hacia el infinito negativo) que define el % como un operador de resto en lugar de un operador de módulo.

  • El resto es el resultado de la operación de módulo por definición. No debería existir el operador de resto porque no existe la operación de resto, se llama módulo.

    – gronostaj

    27/06/2015 a las 20:49

  • @gronostaj no en CS. Mire lenguajes de nivel superior como Haskell o Scheme que definen dos operadores diferentes (remainder y modulo en esquema, rem y mod en Haskell). Las especificaciones de estos operadores difieren en estos lenguajes sobre cómo se realiza la división: truncamiento hacia 0 o hacia infinito negativo. Por cierto, el estándar C nunca llama al % la operador de módulosimplemente lo nombran % operador.

    – ouah

    21 de diciembre de 2015 a las 16:55

  • No debe confundirse con el remainder función en C, que implementa el resto IEEE con semántica de redondeo hacia el más cercano en la división

    –Eric

    18 de septiembre de 2017 a las 2:11


  • @gronostaj El enlace que proporcionó hace específicamente la distinción entre resto mínimo positivoy mínimo resto absoluto lo que obviamente no siempre es positivo. Da -2 como el menor resto absoluto de 43 / 5 (ya que 43 = 9 * 5 - 2). La definición de CS es una vez más diferente. Pero vale la pena señalar que solo porque aprendimos algo cuando teníamos 10 años, todavía puede haber algunas sutilezas. Tratar round(2.5) en Python, por ejemplo. Es 2, no 3. Y eso es matemáticamente correcto, para evitar sesgos en momentos estadísticos.

    –Mike Williamson

    18 de febrero de 2021 a las 17:25

avatar de usuario
ArjunShankar

C99 requiere que cuando a/b es representable:

(a/b) * b + a%b será igual a

Esto tiene sentido, lógicamente. ¿Derecha?

Veamos a qué conduce esto:


Ejemplo A. 5/(-3) es -1

=> (-1) * (-3) + 5%(-3) = 5

Esto solo puede suceder si 5%(-3) es 2


Ejemplo B. (-5)/3 es -1

=> (-1) * 3 + (-5)%3 = -5

Esto solo puede suceder si (-5)%3 es -2

  • ¿Debería el compilador ser lo suficientemente inteligente y detectar que un módulo sin firmar otro sin firmar siempre es positivo? Actualmente (bueno, GCC 5.2) el compilador parece pensar que “%” devuelve un “int” en este caso, en lugar de “sin firmar”, incluso cuando ambos operandos son uint32_t o más grandes.

    – Federico Norte

    24/09/2016 a las 14:56

  • @FrederickNord ¿Tiene un ejemplo para mostrar ese comportamiento?

    – chux – Reincorporar a Monica

    24 de febrero de 2017 a las 16:16


  • Comprenda que lo que describe es la descripción habitual int (a / b) (truncada) de mod. Pero también es posible que la regla sea piso(a/b) (Knuth). En el caso Knuth -5/3 es -2 y el mod se convierte en 1. En resumen: un módulo tiene un signo que sigue al signo del dividendo (truncado), el otro módulo tiene un signo que sigue al signo del divisor (Knuth).

    – Isaac

    27 de mayo de 2018 a las 9:36

  • Este es un caso en el que el estándar C no es exactamente lo que quiero. Nunca he querido truncar a cero o números de módulo negativos, pero a menudo quiero lo contrario y necesito solucionar C.

    – José

    6 de agosto de 2018 a las 22:27

  • @Nick el a/b en la expresión (a/b) * b + a%b arriba es la división de enteros, por lo que (a/b) * b no es igual a a a no ser que a es divisible por b.

    – Laurence Gonsalves

    17 dic 2021 a las 17:51

avatar de usuario
dewang

Basado en la especificación C99: a == (a / b) * b + a % b

Podemos escribir una función para calcular (a % b) == a - (a / b) * b!

int remainder(int a, int b)
{
    return a - (a / b) * b;
}

Para la operación de módulo, podemos tener la siguiente función (asumiendo b > 0)

int mod(int a, int b)
{
    int r = a % b;
    return r < 0 ? r + b : r;
}

mi conclusión es que a % b en C es una operación de resto y NO una operación de módulo.

  • Esto no da resultados positivos cuando b es negativo (y de hecho para r y b ambos negativos da resultados menos que -b). Para garantizar resultados positivos para todas las entradas que podría utilizar r + abs(b) o para hacer coincidir bs signo que podría cambiar la condición a r*b < 0 en cambio.

    – Martín Ender

    20 de septiembre de 2016 a las 11:42

  • @MartinEnder r + abs(b) es UB cuando b == INT_MIN.

    – chux – Reincorporar a Monica

    27 de septiembre de 2018 a las 4:20

avatar de usuario
Udayraj Deshmukh

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: Asumiendo N > 0 y N + N - 1 <= INT_MAX

int modulo(int x,int N){
    return (x % N + N) %N;
}

Esto funcionará para tanto positivo como negativo valores de x.

PD original: también como lo señaló @chux, si su x y N pueden alcanzar algo como INT_MAX-1 e INT_MAX respectivamente, simplemente reemplace int con long long int.

Y si también cruzan los límites de long long (es decir, cerca de LLONG_MAX), entonces deberá manejar los casos positivos y negativos por separado como se describe en otras respuestas aquí.

De acuerdo a estándar C99sección 6.5.5 Operadores multiplicativosse requiere lo siguiente:

(a / b) * b + a % b = a

Conclusión

El signo del resultado de una operación de resto, según C99, es el mismo que el del dividendo.

Veamos algunos ejemplos (dividend / divisor):

Cuando solo el dividendo es negativo

(-3 / 2) * 2  +  -3 % 2 = -3

(-3 / 2) * 2 = -2

(-3 % 2) must be -1

Cuando solo el divisor es negativo

(3 / -2) * -2  +  3 % -2 = 3

(3 / -2) * -2 = 2

(3 % -2) must be 1

Cuando tanto el divisor como el dividendo son negativos

(-3 / -2) * -2  +  -3 % -2 = -3

(-3 / -2) * -2 = -2

(-3 % -2) must be -1

6.5.5 Operadores multiplicativos

Sintaxis

  1. expresión-multiplicativa:
    • cast-expression
    • multiplicative-expression * cast-expression
    • multiplicative-expression / cast-expression
    • multiplicative-expression % cast-expression

Restricciones

  1. Cada uno de los operandos tendrá tipo aritmético. Los operandos de la % El operador debe tener un tipo entero.

Semántica

  1. Las conversiones aritméticas habituales se realizan en los operandos.

  2. El resultado del binario. * El operador es el producto de los operandos.

  3. el resultado de la / operador es el cociente de la división del primer operando por el segundo; el resultado de la % el operador es el resto. En ambas operaciones, si el valor del segundo operando es cero, el comportamiento es indefinido.

  4. Cuando se dividen números enteros, el resultado de la / operador es el cociente algebraico con cualquier parte fraccionaria descartada [1]. Si el cociente a/b es representable, la expresión (a/b)*b + a%b será igual a.

[1]: Esto a menudo se denomina “truncamiento hacia cero”.

¿Puede un módulo ser negativo?

% puede ser negativo ya que es el operador resto, el resto después de la división, no después División_euclidiana. Desde C99 el resultado puede ser 0, negativo o positivo.

 // a % b
 7 %  3 -->  1  
 7 % -3 -->  1  
-7 %  3 --> -1  
-7 % -3 --> -1  

Él módulo OP buscado es un clasico módulo euclidianono %.

Esperaba un resultado positivo cada vez.

Para realizar un módulo euclidiano que esté bien definido siempre que a/b se define, a,b son de cualquier signo y el resultado nunca es negativo:

int modulo_Euclidean(int a, int b) {
  int m = a % b;
  if (m < 0) {
    // m += (b < 0) ? -b : b; // avoid this form: it is UB when b == INT_MIN
    m = (b < 0) ? m - b : m + b;
  }
  return m;
}

modulo_Euclidean( 7,  3) -->  1  
modulo_Euclidean( 7, -3) -->  1  
modulo_Euclidean(-7,  3) -->  2  
modulo_Euclidean(-7, -3) -->  2   

avatar de usuario
yuhao

Las otras respuestas han explicado en C99 o posterior, la división de números enteros que involucran operandos negativos siempre truncar hacia cero.

Tenga en cuenta que, en C89, si el redondeo del resultado hacia arriba o hacia abajo está definido por la implementación. Porque (a/b) * b + a%b es igual a en todos los estándares, el resultado de % la participación de operandos negativos también está definida por la implementación en C89.

¿Ha sido útil esta solución?