Pregunta

Tengo el siguiente código C:

#define PRR_SCALE 255
...
uint8_t a = 3;
uint8_t b = 4;
uint8_t prr;
prr = (PRR_SCALE * a) / b;
printf("prr: %u\n", prr);

Si puedo compilar este (utilizando un compilador plataforma msp430, para un pequeño sistema operativo integrado llamado contiki ) el resultado es 0, mientras que esperaba 191. (Uint8_t se typedef'ed como un unsigned char)

Si lo cambio a:

uint8_t a = 3;
uint8_t b = 4;
uint8_t c = 255;
uint8_t prr;
prr = (c * a) / b;
printf("prr: %u\n", prr);

funciona de manera correcta e imprime 191.

La compilación de una versión simple de este 'normalmente' se utiliza gcc en una caja de Ubuntu imprime el valor correcto en ambos casos.

No estoy exactamente seguro de por qué esto es. Pude evitarlo mediante la asignación del valor definido a una variable de antemano, pero yo prefiero no hacerlo.

¿Alguien sabe qué es esto? Tal vez con un enlace a algo más de información acerca de esto?

¿Fue útil?

Solución

La respuesta corta: que el compilador está libre de errores. (No hay ningún problema con el desbordamiento, como otros sugirieron.)

En ambos casos, la aritmética se realiza en int, que se garantiza que sea al menos 16 bits de largo. En el primer fragmento es porque es un 255 int, en este último es debido a promoción integral .

Como se anotó, gcc maneja esto correctamente.

Otros consejos

255 se está procesando como un entero literal y hace que toda la expresión que se basa int en lugar de basarse unsigned char. El segundo caso se obliga al tipo que es correcta. Intente cambiar su # define de la siguiente manera:

 #define PRR_SCALE ((uint8_t) 255)

Si el compilador en cuestión es el mspgcc, se debe apagar una lista ensamblador del programa compilado junto con el archivo binario / hexadecimal. Otros compiladores pueden requerir parámetros del compilador adicionales para hacerlo. O tal vez incluso un desensamblador separada ejecuta en el binario.

Este es el lugar donde debe buscar una explicación. Debido a las optimizaciones del compilador, el código real presentada al procesador podría tener no mucho parecido con el código original de C (pero normalmente hace el mismo trabajo).

Pasando a través de las pocas instrucciones de ensamblador que representa el código defectuoso debe revelar la causa del problema.

Mi conjetura es que el compilador de alguna manera optimiza todo el cálculo de la constante definida SICE es una parte conocida en tiempo de compilación. 255 * x podría ser optimizado para x << 8-x (que es más rápido y más pequeño) Tal vez algo va mal con el código ensamblador optimizado.

Me tomé el tiempo para compilar las dos versiones en mi sistema. Con la optimización de activos, el mspgcc produce el siguiente código:

#define PRR_SCALE 255
uint8_t a = 3;
uint8_t b = 4;
uint8_t prr;
prr = (PRR_SCALE * a) / b;
    40ce:   3c 40 fd ff     mov #-3,    r12 ;#0xfffd
    40d2:   2a 42           mov #4, r10 ;r2 As==10
    40d4:   b0 12 fa 6f     call    __divmodhi4 ;#0x6ffa
    40d8:   0f 4c           mov r12,    r15 ;
printf("prr: %u\n", prr);
    40da:   7f f3           and.b   #-1,    r15 ;r3 As==11
    40dc:   0f 12           push    r15     ;
    40de:   30 12 c0 40     push    #16576      ;#0x40c0
    40e2:   b0 12 9c 67     call    printf      ;#0x679c
    40e6:   21 52           add #4, r1  ;r2 As==10

Como podemos ver, el compilador calcula directamente el resultado de 255 * 3 para -3 (0xfffd). Y aquí esta el problema. De alguna manera el 255 consigue interpretarse como -1 signo de 8 bits en lugar de 255 sin signo de 16 bits. O que se analiza a 8 bits primero y luego signo extendido a 16 bits. o lo que sea.

Una discusión sobre este tema se ha iniciado en la lista de correo ya mspgcc.

No estoy seguro de por qué la definen no funciona, pero es posible que se golpea a los vuelcos con las variables uint8_t. 255 es el valor máximo para uint8_t (2^8 - 1), por lo que si se multiplica por 3, que está obligado a ejecutar en algunos problemas de vuelco sutiles.

El compilador podría ser la optimización de su código, y pre-cálculo del resultado de la expresión matemática y empujando el resultado en PRR (ya que se ajusta, a pesar de que el valor intermedio no encaja).

Comprobar lo que sucede si se rompe su expresión como esta (esto no se comportará como lo que quiere):

prr = c * a; // rollover!
prr = prr / b;

Es posible que necesite sólo tiene que utilizar un tipo de datos más grande.

Una diferencia que se me ocurre en el caso-1 es,

El valor literal PRR_SCALE puede entrar en ROM o el área de código. Y puede haber alguna diferencia en el opecode MUL por ejemplo,

case-1: [register], [rom]
case -2: [register], [register]

Puede que no tiene sentido en absoluto.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top