É esta função C escrito em forma de pobres?
-
12-09-2019 - |
Pergunta
char byte_to_ascii(char value_to_convert, volatile char *converted_value) {
if (value_to_convert < 10) {
return (value_to_convert + 48);
} else {
char a = value_to_convert / 10;
double x = fmod((double)value_to_convert, 10.0);
char b = (char)x;
a = a + 48;
b = b + 48;
*converted_value = a;
*(converted_value+1) = b;
return 0;
}
}
O objectivo desta função é a de ter um valor unsigned char de 0 a 99 e retornar Ou é ascii equivalente no caso de ser 0-9 ou manipular uma pequena matriz de caracteres global que pode ser referenciada a partir da função seguinte código de chamada conclusão.
Faço esta pergunta porque dois compiladores do mesmo fornecedor interpretar este código de maneiras diferentes.
Este código foi escrito como uma forma de endereço parse bytes enviados via RS485 em strings que podem ser facilmente passados ??para uma função send-lcd-string.
Este código é escrito para a arquitetura PIC18 (8 bits uC).
O problema é que a versão gratuita / avaliação de um compilador particular, gera código de montagem perfeita que as obras, enquanto que sofrem um impacto no desempenho, mas o compilador pago e supostamente superior gera um código mais eficiente à custa de ser referência capazes os endereços de todos meus matrizes de bytes usado para conduzir os gráficos na tela da minha lcd.
Eu sei que eu estou colocando muita lama na água usando um compilador proprietário para um menos de arquitectura típica, mas espero que alguém lá fora, tem algumas sugestões.
Graças.
Solução
modulo e divisão inteira pode ser muito, muito caro. Eu tenho não sei sobre a sua arquitetura particular, mas meu palpite lá é caro também.
Se você precisa de ambos, divisão e módulo, fazer um deles e obter o outro pela multiplicação / diferença.
q =p/10;
r = p - q*10;
Outras dicas
Eu definitivamente evitar o uso de ponto flutuante qualquer coisa em um PIC. E eu gostaria de -experimentar não to- usar quaisquer divisões. Quantas vezes você vê o envio de um caractere não-ascii ao LCD? pode guardá-lo para a memória do LCD e, em seguida, chamá-lo por sua posição de memória?
Aqui está o que uma divisão por 10 looks como no meu código, observe os 17 ciclos de que necessita para ser concluído. Pense em quanto tempo isso vai demorar, e certifique-se não há mais nada de espera sobre isso.
61: q = d2 / 10;
01520 90482E mov.b [0x001c+10],0x0000
01522 FB8000 ze 0x0000,0x0000
01524 2000A2 mov.w #0xa,0x0004
01526 090011 repeat #17
01528 D88002 div.uw 0x0000,0x0004
0152A 984F00 mov.b 0x0000,[0x001c+8]
Se você fizer uma coisa em ponto flutuante no seu código, procure na memória do programa depois que você compilou, na guia simbólico (para que você possa realmente lê-lo) e olhar para o código de ponto flutuante que terá de ser incluído. Você vai encontrá-lo perto do topo (dependendo do seu código), em breve (ish) após o rótulo _reset.
Mina começa no número 223 e memória linha de endereço do 001BC com _ floatsisf, continua através de vários rótulos adicionais (_fpack, _divsf3, etc) e termina em _funpack, última linha a 535 e 0042C endereço de memória. Se você pode lidar com (42C-1BC = 0x270 =) 624 bytes de espaço programa perdido, grande, mas alguns chips têm apenas 2k de espaço e isso não é uma opção.
Em vez de ponto flutuante, se for possível, tente usar aritmética de ponto fixo, na base 2.
Tanto quanto não ser capaz de referenciar todas as matrizes de bytes em seu LCD, você tem verificado para se certificar de que você não está tentando enviar um nulo (que é o endereço de um bem) mas obter do parado por verificação de código para o fim de uma seqüência de caracteres ASCII? (Isso aconteceu comigo antes).
eu provavelmente escrever que, como:
char byte_to_ascii(char value_to_convert, volatile char *converted_value)
{
if (value_to_convert < 10) {
return value_to_convert + '0';
} else {
converted_value[0] = (value_to_convert / 10) + '0';
converted_value[1] = (value_to_convert % 10) + '0';
return 0;
}
}
É má forma para converter em flutuante, fmod chamada, e convertido para inteiro, em vez de apenas usando o operador%? Eu diria que sim. Há maneiras mais legíveis para abrandar um programa para atender a algumas exigência de tempo, por exemplo dormir em um loop. Não importa o compilador ou o ajuste de código de montagem ou qualquer outra coisa, esta é uma maneira altamente ofuscado para controlar a velocidade de execução do seu programa, e eu chamá-lo de forma pobre.
Se os meios de código de montagem perfeitas que ele funciona bem, mas é ainda mais lento do que as conversões de ponto flutuante e para trás, em seguida, usar inteiros e dormir em um loop.
Quanto ao código de montagem imperfeita, qual é o problema? "À custa de ser referência capazes os endereços de todos os meus matrizes de bytes"? Parece que tipo char * está trabalhando em seu código, por isso parece que você pode resolver todas as suas matrizes de bytes a forma como o padrão C diz que você pode. Qual é o problema?
Francamente, eu diria que sim ..
Se você quisesse b para ser o restante, use MOD ou roll-seu-próprio:
char a = value_to_convert / 10;
char b = value_to_convert - (10 * a);
Conversão de / para flutuadores nunca é a maneira de fazer as coisas, a menos que seus valores são realmente carros alegóricos.
Além disso, eu recomendo fortemente a vara para a convenção de se referir explicitamente a seus tipos de dados como 'assinado' ou 'sem sinal', e deixar o 'char' nua para quando ele realmente é um personagem (parte de uma string). Você está passando em dados brutos, o que me parece que deve ser um unsigned char (assumindo, claro, que a fonte é sem sinal!). É fácil esquecer-se se algo deve ser assinado / sem assinatura e com um char nua, você vai ter todos os tipos de erros roll-over.
A maioria dos micros de 8 bits demorar uma eternidade para uma multiplicação (e mais do que sempre para uma divisão), e assim tentar minimizar estes.
Espero que isso ajude ..
O código parece estar fazendo duas coisas muito diferentes, dependendo se ele é dado um número no intervalo 0-9 ou 10-99. Por essa razão, eu diria que esta função é escrita em forma de pobres: Eu iria dividir a função em duas funções
.Uma vez que estamos discutindo divisões por 10 aqui ..
Esta é a minha opinião. É apenas operações simples e nem sequer precisa de registros de largura.
unsigned char divide_by_10 (unsigned char value)
{
unsigned char q;
q = (value>>1) + (value>>2);
q += (q>>4);
q >>= 3;
value -= (q<<3)+q+q;
return q+((value+6)>>4);
}
Cheers, Nils
É típico para otimizadores fazer coisinhas indesejados ao longo do tempo se você fuçar nas partes internas.
é seu converted_value um valor global ou de outra forma atribuído de forma tão a que o compilador não sabe tocá-lo?
PIC não gosta de fazer aritmética de ponteiro.
Como o Windows pontos fora programador, utilize o operador mod (veja abaixo).
char byte_to_ascii(char value_to_convert, volatile char *converted_value) {
if (value_to_convert < 10) {
return (value_to_convert + 48);
} else {
char a = value_to_convert / 10;
char b = value_TO_convert%10;
a = a + 48;
b = b + 48;
*converted_value = a;
*(converted_value+1) = b;
return 0;
}
}
Sim, eu acredito que a sua função:
char byte_to_ascii(char value_to_convert, volatile char *converted_value) {
if (value_to_convert < 10) {
return (value_to_convert + 48);
} else {
char a = value_to_convert / 10;
double x = fmod((double)value_to_convert, 10.0);
char b = (char)x;
a = a + 48;
b = b + 48;
*converted_value = a;
*(converted_value+1) = b;
return 0;
}
}
is in poor form:
Don't use decimal numbers for ASCII chars, use the character, i.e. '@' instead of 0x40.
There is no need for using the fmode
function.
Here is my example:
// Assuming 8-bit octet
char value;
char largeValue;
value = value_to_convert / 100;
value += '0';
converted_value[0] = value;
largeValue = value_to_convert - value * 100;
value = largeValue / 10;
value += '0';
converted_value[1] = value;
largeValue = largeValue - value * 10;
value += '0';
converted_value[2] = value;
converted_value[3] = '\0'; // Null terminator.
Since there are only 3 digits, I decided to unroll the loop. There are no branches to interrupt the prefetching of instructions. No floating point exceptions, just integer arithmetic.
If you leading spaces instead of zeros, you can try this:
value = (value == 0) ? ' ' : value + '0';
Apenas para ser um nitwitt, mas várias instruções return
da mesma função pode ser considerado má forma (MISRA).
Além disso, algumas das discussões acima estão no limite de otimizações permature. Algumas tarefas deve ser deixada para o compilador. No entanto, em um ambiente incorporado, minimalista, estes truques pode ser válido ainda.