Então aqui está um exemplo.
#include <stdio.h>
int main(void){
int score1 = 70, score2 = 58, score3 = 99;
float total = 3;
printf("Average: %f\n", (score1 + score2 + score3)/total);
}
e isso me dá 75.666664
. enquanto se eu substituir total por apenas 3.0 como este código
#include <stdio.h>
int main(void){
int score1 = 70, score2 = 58, score3 = 99;
printf("Average: %f\n", (score1 + score2 + score3)/3.0);
}
isso me dá75.666667.
Tudo em C tem um tipo e isso inclui constantes/literais numéricos como
3.0
.3
tem tipoint
.3.0f
tem tipofloat
.3.0
tem tipodouble
.E por sua vez,
float
edouble
pode ou não ter a mesma precisão em um determinado sistema. Aparentementedouble
tem maior precisão no seu sistema e por isso você nota uma diferença.Algumas notas sobre promoções de tipo implícito :
score1 + score2 + score3
são todos do tipoint
, portanto não ocorrem promoções implícitas e o resultado éint
.Este resultado
int
colocado junto em uma operação com outro operando do tipofloat
, significa que oint
será promovidofloat
antes da operação (conforme "as conversões aritméticas usuais", veja o link acima).Ou no caso de
3.0
, oint
resultado teria sido promovido paradouble
.printf
e%f
faça o que acontece, sempre espere adouble
as input (%lf
funcionará também) e, caso você entregue afloat
, ele será promovido implicitamentedouble
por uma regra excêntrica para funções variadas chamada "promoções de argumento padrão".Mas como esse tipo de promoção final acontece após o cálculo do resultado, isso não afeta a precisão.
Melhores Práticas:
int
//float
nadouble
mesma expressão, pois essa é uma receita bem conhecida para bugs. Atenha-se a um tipo e lance operandos explicitamente quando necessário.Por exemplo, eu reescreveria seu código assim:
A funcionalidade é 100% equivalente, mas este é um código autodocumentado sem depender de promoção implícita dizendo "Eu sei o que estou fazendo".
O oposto - código baseado em promoções implícitas silenciosas - diz: "Ou eu sei o que estou fazendo e confio na promoção implícita, ou não sei o que estou fazendo e acertei/errei esta expressão por (ruim) sorte".
Isso não acontece.
Seu primeiro exemplo divide a soma por um float 3.0f de 32 bits e calcula a aproximação mais próxima de 227/3.0f que o cálculo de ponto flutuante de precisão única pode representar.
Seu segundo exemplo divide a soma por um duplo 3.0 (implícito) de 64 bits e obtém a aproximação mais próxima de 227/3.0 que a precisão dupla pode representar.
A distinção seria muito mais clara se você imprimisse no formato% 26,18g.
Eu sugiro que você coloque 75.666666666666666666666 no site decimal para binário Base Convert IEEE754 para ver como isso funciona em representações FP binárias práticas sob IEEE754.
Aliás, muitos compiladores de otimização modernos traduzirão a divisão por uma constante em tempo de compilação em multiplicação pelo valor constante recíproco pré-computado "1.0f/3.0f" porque é muito mais rápido que a divisão. Verifique a listagem do assembler para ver se esse é o caso do seu compilador.