Pergunta

Eu tenho o seguinte 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);

Se eu compilar este (usando um compilador msp430 plataforma, para um pequeno OS incorporado chamado contiki ) o resultado é 0, enquanto eu esperava 191. (Uint8_t é typedef'ed como um unsigned char)

Se eu mudá-lo para:

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

ele funciona corretamente e impressões 191.

Compilar uma versão simples deste 'normalmente' usando gcc em uma caixa de Ubuntu imprime o valor correto em ambos os casos.

Eu não sou exatamente certo por que isso é. Eu poderia contorná-la, atribuindo o valor definido para uma variável de antemão, mas eu prefiro não fazer isso.

Alguém sabe por que isso é? Talvez com um link para mais algumas informações sobre isso?

Foi útil?

Solução

A resposta curta: você compilador é buggy. (Não há nenhum problema com o excesso, como outros sugeriram).

Em ambos os casos, a aritmética é feito em int, que é garantida para ser pelo menos 16 bits de comprimento. No primeiro trecho é porque 255 é um int, neste último é por causa de promoção integral .

Como você observou, alças gcc isso corretamente.

Outras dicas

255 está a ser processado como um número inteiro literal e faz com que toda a expressão a ser int baseado em vez de unsigned char base. A segunda forças de caso do tipo a ser corretas. Tente alterar o # define da seguinte forma:

 #define PRR_SCALE ((uint8_t) 255)

Se o compilador em questão é o mspgcc, ele deve colocar para fora um montador listagem do programa compilado em conjunto com o arquivo binário / hexadecimal. Outros compiladores podem exigir bandeiras do compilador adicionais para fazê-lo. Ou talvez até mesmo uma corrida desmontador separado no binário.

Este é o lugar onde procurar uma explicação. Devido a otimizações do compilador, o código real apresentado ao processador pode não têm muita semelhança com o código original C (mas normalmente faz o mesmo trabalho).

Percorrendo as poucas instruções assembler representando o código defeituoso deve revelar a causa do problema.

Meu palpite é que o compilador de alguma forma otimiza todo o cálculo sice a constante definida é uma parte conhecida em tempo de compilação. 255 * x poderia ser optimizado para x << 8-x (o qual é mais rápido e menor) Talvez algo está errado com o código assembler otimizado.

eu levei um tempo para compilar ambas as versões no meu sistema. Com a otimização de ativos, o mspgcc produz o seguinte 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, o compilador calcula diretamente o resultado de 255 * 3 a -3 (0xFFFD). E aqui está o problema. De alguma forma, a 255 é interpretado como -1 sinal de 8 bits em vez de 255 sem sinal de 16 bits. Ou ele é analisado para 8 bits em primeiro lugar e, em seguida, assinar estendido para 16 bits. ou o que quer.

A discussão sobre este tema foi iniciado na lista mspgcc discussão já.

Eu não tenho certeza por isso que a definição não funciona, mas você pode estar executando em sobreposições com as variáveis ??uint8_t. 255 é o valor máximo para uint8_t (2^8 - 1), por isso, se você multiplicar isso por 3, você é obrigado a funcionar em alguns problemas de sobreposição sutil.

O compilador pode ser otimizar seu código, e pré-cálculo do resultado da sua expressão matemática e empurrando o resultado em PRR (desde que ele se encaixa, mesmo que o valor intermediário não se encaixa).

Verifique o que acontece se você quebrar a sua expressão como esta (isto não se comportará como o que você quer):

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

Você pode precisar usar apenas um tipo de dados maior.

Uma diferença que eu posso pensar no caso-1 é,

O valor literal PRR_SCALE pode entrar em ROM ou código de área. E pode haver alguma diferença na opecode MUL para dizer,

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

Pode não fazer sentido.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top