Por que% f imprime valores grandes quando constantes de ponto flutuante são passadas em vez de variáveis?

9

No programa fornecido, por que obtive resultados diferentes para cada um dos printfs?

#include <stdio.h>
int main()
{
    float c = 4.4e10;
    printf("%f\n", c);
    printf("%f\n", 4.4e10);
    return 0;
}

E mostra a seguinte saída:

44000002048.000000
44000000000.000000
user10056563
fonte
4
As respostas até agora explicam que 4.4e10é uma doubleconstante que é convertida floatna inicialização de, cmas mantida como doublequando passada para printf. No entanto, você também pode querer saber que adicionar um fsufixo o torna uma floatconstante: a impressão 4.4e10fmostrará o mesmo valor resultante da inicialização cpara 4.4e10f. Distinguir floatconstantes de doubleconstantes pode ser importante para realizar um trabalho de qualidade com aritmética de ponto flutuante.
Eric Postpischil 1/10/19
Esse método de conversão tem nome? Eu quero ler sobre isso.
user10056563
Deseja saber quando a conversão de doublepara floatocorre no idioma C? Ou você deseja saber quais valores resultam da conversão, ou seja, quais efeitos a conversão tem? Ou alguma outra coisa?
Eric Postpischil 01/10/19
Não estou questionando nem as respostas aqui nem o padrão, mas quando eu era jovem e aprendia Cque usamos printf("%f",x)para um floate printf("%lf",x)para um double. Quando as coisas mudaram? E como imprimir explicitamente um (único) float- printf("%hf",x)??
Adrian Mole
2
@ Adrian %lfem printf é a mesma coisa que %f. A floatem um argumento variável é convertido em a doublepelo compilador, assim como a shorté convertido em uma int.
SS Anne

Respostas:

9

A floaté um tipo que contém um número de ponto flutuante de 32 bits, enquanto a constante 4.4e10representa a double, que contém um número de ponto flutuante de 64 bits (ou seja, um número de ponto flutuante de dupla precisão)

Quando você atribui 4.4e10a c, o valor 4.4e10não pode ser representado com precisão (um erro de arredondamento em um parâmetro chamado mantissa) e o valor mais próximo possível (44000002048) é armazenado. Quando é passado para printf, é promovido de volta para double, incluindo o erro de arredondamento.

No segundo caso, o valor é doubleo tempo todo, sem estreitar e alargar, e acontece que a doublepode representar exatamente o valor.

Se este é um comportamento indesejável, você pode declarar ccomo doublepara um pouco mais de precisão (mas cuidado que você ainda vai bater limites de precisão eventualmente).

nanofarad
fonte
3

Você está realmente imprimindo os valores de dois tipos diferentes aqui.

No primeiro caso, você está atribuindo um valor a uma variável do tipo float. A precisão de a floaté aproximadamente 6 ou 7 dígitos decimais, portanto, a menos que o valor possa ser representado exatamente, você verá o valor mais próximo que pode ser representado por esse tipo.

No segundo caso, você está passando a constante 4.4e10que tem o tipo double. Esse tipo tem cerca de 16 dígitos decimais de precisão e o valor está dentro desse intervalo, portanto, o valor exato é impresso.

dbush
fonte
Por que especificamente imprime 2048 no final?
user10056563
@ user10056563 Porque esse é o número mais próximo do 4.4e10 que pode ser armazenado em um flutuador de 32 bits.
dbush