Qual é a maneira correta de multiplicar e dividir ints em C para evitar estouro? Quero determinar quanto tempo ticks
um temporizador rodando em freq
(em Hz) levará para fazer a delay
(em ms). Isso deveria ser ticks = freq * delay / 1000
.
Mas, essa linha parece perigosa para mim. Se escrevermos (freq * delay) / 1000
corremos o risco de transbordar. Se, em vez disso, escrevermos freq * (delay / 1000)
, entraremos em carros alegóricos - que são desnecessários e propensos a erros, especialmente. em um microcontrolador.
Qual é a maneira correta de fazer isso?
Sua sugestão:
Não é uma operação de ponto flutuante. Isso exigiria:
Mas você está certo de que não é necessário. Muito melhor seria:
Contanto que
freq
seja um múltiplo de 1000, isso não resultará em perda de precisão e evitará pelo menos um estouro "prematuro" (ou seja, um estouro que ocorre em valores mais baixos devido a uma má escolha da ordem de operação).É claro que um overflow continua possível, mas esta expressão fornece o alcance máximo possível sem
delay
recorrer a um tipo maior. Por exemplo, se a expressão for do tipouint32_t
,delay
pode ter até 2 32/1000 ou quase 72 minutos.Criticamente, a reordenação permite que o intervalo
delay
seja determinístico em qualquer sistema. Não depende mais do valor defreq
- serão sempre 72 minutos.Se 72 minutos não forem suficientes, você poderá considerar um atraso de resolução menor (segundos inteiros, por exemplo) antes de recorrer a um tipo de dados maior (que será menos eficiente e poderá adicionar problemas de atomicidade). Atrasos longos raramente exigem precisão de milissegundos, e seu relógio pode ter precisão de milissegundos durante esse período de tempo em qualquer caso - até mesmo os TCXO têm precisão de normalmente 2 ppm.
Simplesmente, pense em quanto tempo máximo de atraso você pode ter.
Se estiver usando um número não assinado de 32 bits (números negativos para atraso não são muito práticos porque não sei como ter um atraso negativo que nos levaria de volta no tempo). O uint32_t máximo é 4.294.967.295, o que é suficiente para atraso de mais de uma hora.
Se você definir seu cronômetro para aumentar a cada 1/1000 segundos, uint32_t será suficiente para atrasar
49.7102696181
diasSe precisar de mais, use um número inteiro sem sinal maior em seus cálculos:
((uint64_t)freq * delay) / 1000