Qual a diferença entre os tipos int, float e double em C?

Passo 1

O que a pergunta pede: explicar claramente as diferenças entre os tipos de dados básicos em C: int, float e double.
Informações relevantes que já temos (implícitas na pergunta): são tipos primitivos da linguagem C usados para armazenar números; cada um tem tamanho, precisão e comportamento diferentes. Vou listar essas diferenças passo a passo, com exemplos e dicas práticas.

Passo 2

O que é int — conceito, tamanho e uso
  • Definição simples: int armazena números inteiros (sem parte fracionária). Ex.: \texttt{int a = 5;}.
  • Tamanho: a norma C garante um tamanho mínimo, mas na prática em máquinas modernas típicas o int tem 4 bytes (ou seja, 32 bits). Isso pode variar (em algumas arquiteturas pode ser 2 bytes, raramente outro). Para saber no seu compilador use \texttt{sizeof(int)}.
  • Intervalo (para 32-bit, típico): de -2^{31} até 2^{31}-1, ou seja de -2\,147\,483\,648 até 2\,147\,483\,647.
  • Comportamento em overflow: se somar além do máximo, ocorre overflow (undefined behavior se for signed overflow) — cuidado!
  • Exemplos de uso: contadores, índices de arrays, quantidades discretas.
  • Observação sobre variantes: existem \texttt{unsigned int}, \texttt{long}, \texttt{short} — cada um muda tamanho/intervalo.

Passo 3

O que é float — representação, precisão e limitações
  • Definição simples: float armazena números reais (com parte fracionária) em precisão de ponto flutuante de precisão simples.
  • Representação típica: segue IEEE-754 single precision em praticamente todas as arquiteturas modernas: 32 bits no total, divididos em 1 bit de sinal, 8 bits de expoente e 23 bits de mantissa/fracionária.
  • Precisão: cerca de 6 a 7 dígitos decimais significativos. Isso significa que números com mais dígitos significativos perdem precisão.
  • Alcance do expoente: aproximadamente até \pm 10^{\pm 38} (ordem de magnitude).
  • Exemplo prático de perda de precisão: se você guardar \texttt{float x = 12345678.0f;} os últimos dígitos podem não ser representados exatamente porque ultrapassa a precisão de \sim 7 dígitos.
  • Impressão e leitura: em \texttt{printf}, argumentos de tipo \texttt{float} são promovidos para \texttt{double} nas funções variádicas, então usa-se \texttt{"%f"} para imprimir; em \texttt{scanf} use \texttt{"%f"} para ler um \texttt{float} (note a diferença).
  • Use quando: memória é crítica (grandes vetores em GPU/embedded) ou quando a precisão menor é aceitável e se deseja velocidade/menor uso de memória.

Passo 4

O que é double — representação, precisão e vantagens
  • Definição simples: double também armazena números reais, mas com precisão dupla.
  • Representação típica: IEEE-754 double precision: 64 bits totais, com 1 bit de sinal, 11 bits de expoente e 52 bits de mantissa.
  • Precisão: cerca de 15 a 17 dígitos decimais significativos — muito superior ao float.
  • Alcance do expoente: aproximadamente até \pm 10^{\pm 308}.
  • Literais em C: um literal como \texttt{1.23} é do tipo \texttt{double} por padrão; para criar um \texttt{float} literal use \texttt{1.23f}.
  • Impressão e leitura: \texttt{printf("%f", valor);} funciona para \texttt{double} (e \texttt{float} é promovido a \texttt{double} em \texttt{printf}). Em \texttt{scanf}, use \texttt{"%lf"} para \texttt{double}.
  • Use quando: precisa de precisão numérica confiável em cálculos científicos, financeiros (com cautela) e em quase toda rotina numérica geral.

Passo 5

Comparações práticas: precisão, tamanho e desempenho
  • Tamanho em bytes típicos (comum em PCs): sizeof(int) = 4, sizeof(float) = 4, sizeof(double) = 8. (Mas confirme com \texttt{sizeof} no seu sistema.)
  • Precisão: float \approx 7 dígitos decimais; double \approx 15 dígitos decimais.
  • Exemplo intuitivo de diferença: se um float tem 7 dígitos de precisão, então o número 12345678 não é representado com todos os dígitos corretos; um double consegue representar com muito mais precisão números grandes ou frações pequenas.
  • Performance: em CPUs modernos, operações em double frequentemente são quase tão rápidas quanto em float. Em GPUs/embedded o float pode ser muito mais rápido e ocupar menos memória, então a escolha depende do hardware.
  • Erros numéricos: comparações diretas de números de ponto flutuante são perigosas — prefira comparar com uma tolerância (epsilon).

Passo 6

Regras e armadilhas importantes (literais, promoções, scanf/printf, comparação)
  • Literais: \texttt{3.14} é \texttt{double}; \texttt{3.14f} é \texttt{float}; \texttt{3.14L} é \texttt{long double}.
  • Promoção aritmética: em expressões aritméticas, \texttt{float} é promovido a \texttt{double} quando necessário (conversões usuais). Assim, \texttt{float + double -> double}.
  • scanf/printf:
    • \texttt{printf("%f", x);} espera um \texttt{double}; se passar um \texttt{float} ele já foi promovido para \texttt{double}.
    • \texttt{scanf("%f", &x);} lê para \texttt{float *}.
    • \texttt{scanf("%lf", &d);} lê para \texttt{double *}.
  • Comparação com epsilon: para testar igualdade aproximada, faça algo como:
    • \texttt{if (fabs(a - b) < 1e-6) \{} \} (escolha o 10^{-6} conforme a precisão necessária).
  • Overflow vs overflow de ponto flutuante:
    • Para int: overflow signed é UB; unsigned wrap-around definido.
    • Para float/double: existe representação de \texttt{inf} e \texttt{NaN} (não um erro UB).

Passo 7

Exemplos curtos para fixar (lembre-se de testar no seu compilador)
  • Declarações:
    • \texttt{int i = 100;}
    • \texttt{float f = 3.14159f;}
    • \texttt{double d = 3.141592653589793;}
  • Impressão:
    • \texttt{printf("%d\n", i);}
    • \texttt{printf("%f\n", f);} // f é promovido para double
    • \texttt{printf("%lf\n", d);} // “%lf” também funciona no printf em muitos sistemas, mas “%f” é o usual para double; prefira “%f” para printf e “%lf” para scanf.
  • Verificar sizeof:
    • \texttt{printf("int=%zu float=%zu double=%zu\n", sizeof(int), sizeof(float), sizeof(double));}

Passo 8

Recomendações práticas rápidas (quando usar cada um)
  • Use int para valores inteiros (contadores, índices). Se precisar de números inteiros maiores sem sinal, use \texttt{unsigned} ou tipos com largura garantida ( \texttt{int64_t}).
  • Use double como escolha padrão para números reais em aplicações científicas e para evitar perda de precisão.
  • Use float quando memória/performance ou hardware (GPU, DSP, microcontroladores) exigirem, ou quando a precisão de ~7 dígitos for suficiente.

Resposta

int: tipo para números inteiros; tipicamente 4 bytes em arquiteturas modernas (intervalo típico -2^{31} a 2^{31}-1), sem parte fracionária e com risco de overflow em operações que ultrapassam o intervalo.
float: ponto flutuante de precisão simples; tipicamente 32 bits ( 1 bit sinal, 8 bits expoente, 23 bits mantissa); precisão ~ 6 7 dígitos decimais e alcance ~ \pm10^{\pm38}; usa-se \texttt{1.23f} para literais float.
double: ponto flutuante de precisão dupla; tipicamente 64 bits ( 1 bit sinal, 11 bits expoente, 52 bits mantissa); precisão ~ 15 17 dígitos decimais e alcance ~ \pm10^{\pm308}; literal padrão para números de ponto flutuante em C é \texttt{double} ( \texttt{1.23}).

Exercícios relacionados