2012-01-29 8 views
12

Tengo una matriz en C que quiero abordar de manera similar a un búfer circular, por ejemplo: a[-1] me devolvería el último elemento de la matriz.Resultados extraños de la aritmética de módulo con números negativos y denominadores sin signo

para hacer eso traté de usar la aritmética de módulo (obviamente), el problema es, que estoy obteniendo resultados bastante extraños cuando los números negativos están involucrados:

-1 % 4 = -1 
-1 % 4U = 3 

Hasta ahora, todo bien.

-1 % 4000 = -1 
(-1+4000U) % 4000U = 3999 
(-1) % 4000U = 3295 

Pregunta: El valor (3295) no mantener durante el estándar C (a/b)*b + a%b shall equal a, truncated towards zero (por a=-1, b=4000) a partir de (6.5.5 # 6) así que no es un error per se, pero qué es el estándar definido de esta manera ?! Seguramente, debe haber cierta lógica en este ...

¿Cómo tengo que escribir a%b para obtener resultados razonables para a negativo (como (a+b)%b deja de funcionar cuando abs(a)>b)?

aplicación de la prueba:

#include <stdio.h> 
int main(int argc, char **argv) { 
    int i=0; 
#define MAX_NUM 4000U 
    int weird = (i-1)%MAX_NUM; 
    printf("%i\n", weird); 
    printf("%i\n", (i-1+MAX_NUM))%MAX_NUM); 
    printf("a: %i, b: %i, a from equation: %i\n", i-1, MAX_NUM, 
    ((i-1)/MAX_NUM)*MAX_NUM + weird); 
    return 0; 
} 

Respuesta

9

aritmética en C siempre (por debajo de algunas rarezas con operadores de desplazamiento de bits) promueve todos los operandos a un tipo común antes de realizar la operación. Por lo tanto:

(-1) % 4000U 

se promueve como (asumiendo enteros de 32 bits):

0xffffffffu % 4000u 

que produce 3295.

Si desea utilizar la aritmética modular para las compensaciones de matriz que podría ser negativo, primero debe abandonar el uso de aritmética sin signo en los desplazamientos. Como tal, sus resultados estarán ahora en el rango de -MAX_NUM+1 a MAX_NUM-1, debido a la definición fea de C de la división de enteros con signo y el resto. Si el código no es crítico para el rendimiento, simplemente agregue if (result<0) result+=MAX_NUM; y termine con él. Si realmente necesita evitar la sucursal (y tiene medido para determinar que necesita evitarlo) luego pregunte de nuevo cómo optimizar este cálculo y yo mismo o alguien más brillante que yo en SO seguramente podré ayudarlo. :-)

5

Como dice 6.5.3, "Las conversiones aritméticas habituales se realizan en los operandos". En el caso de su ejemplo:

(-1) % 4000U 

que significa la conversión de la -1 en un unsigned int. Su -1, por lo tanto, realmente se interpreta como 4294967295 ... para el cual el resto es exactamente lo que está viendo: 3295.

Las "conversiones aritméticas habituales" se describen en 6.3.1.8.

Cuestiones relacionadas