segunda-feira, 25 de agosto de 2025

Entendendo os tipos primitivos númericos do Java

Os tipos primitivos representam os valores mais básicos e fundamentais que uma linguagem pode manipular. No Java existem 6 tipos primitivos númericos, que são:

  • byte: Representa um inteiro de 8 bits.
  • short: Representa um inteiro de 16 bits.
  • int: Representa um inteiro de 32 bits.
  • long: Representa um inteiro de 64 bits.
  • float: Representa um número de ponto flutuante de 32 bits.
  • double: Representa um número de ponto flutuante de 64 bits.

Uma maneira que eu faço para aprender esses detalhes, é sujar as mãos com teoria e prática.

É muito simples entender os tipos int e float, mas, os outros tipos provavelmente só aceitamos suas definições. O que quero dizer com isso é que se no curso o instrutor falar:

"O tipo byte representa um inteiro de 8 bits" só iremos aceitar que é uma verdade.

Byte

Se você já viu meus posts sobre ASCII, Unicode, Binário... você já deve saber que esse espaço de 8 bits pode ser usado para representar até 256256 caracteres ou outro tipo de informação.

Logo se a especificação do Java diz "Representa um inteiro de 8 bits" ela está querendo dizer que "Representa um número que esteja entre 0 e 255" certo?

Errado.

O Java utiliza números assinados, isso significa que metade das combinações será usada para números negativos e a outra metade para números positivos, incluindo o zero.

No final das contas ele irá usar os espaços de 8 bits para representar números positivos e negativos. Esse intervalo do byte em Java vai de 128−128 até 127127:

!image

Na prática, se você for atribuir um valor para uma variável do tipo byte que seja maior que 127127 ou menor que 128-128 o seu programa irá quebrar (Sua IDE já vai acusar erro de type-casting).

public class DataTypes {

  public static void main(String[] args) {
    byte x = 42; // Válido
    byte y = 129; // Erro
  }
}

Short, Int, Long...

Uma maneira mais simples de descobrir os limites dos outros tipos é com matemática, já que binário usa o sistema de base 2x2^x podemos descobrir os limites fazendo:

28=2562^{8} = 256 → metade para negativos, metade para positivos.

216=65.5362^{16} = 65.536 → metade para negativos, metade para positivos.

232=4.294.967.2962^{32} = 4.294.967.296 → metade para negativos, metade para positivos.

264=18.446.744.073.709.551.6162^{64} = 18.446.744.073.709.551.616 → metade para negativos, metade para positivos.

Com isso, qualquer atribuíção de valor em uma variável com o tipo short, int ou long poderá gerar erro caso esteja fora dos seus respectivos limites.

Como funciona o Float e Double?

Existe um padrão chamado IEEE 754 que o Java usa para lidar com números com ponto flutuante:

  • 32 bits: 1 bit para o sinal (++ ou -), 11 bits de expoente, 23 bits de precisão (Mantissa).

!image

  • 64 bits: 1 bit para o sinal (++ ou -), 11 bits de expoente, 52 bits de precisão (Mantissa).

!image

Agora podemos observar de forma mais clara o motivo do tipo double ser mais preciso que o float, só de bits de precisão o double tem 52.

Resumindo, pra representar um número de ponto flutuante é necessário quebrar nossos 32 bits ou 64 bits em três partes:

  • Sinal
  • Expoente
  • Precisão

A fórmula geral:

Pra chegar nos limites desses tipos envolve mais matemática, por ser números mais complexos:

(1)S×(1+M)×2(EB)(−1)^S × (1+M) × 2^{(E - B)}

  • 1 bit para o sinal (S): 00 para positivo, 11 para negativo
  • Mantissa ou fração (M): tamanho varia
  • Expoente (E): tamanho varia
  • Bias (B) do expoente: 127 para float e 1023 para double

Não recomendo ir mais a fundo que isso, até aqui você já entendeu mais sobre como funciona esses dois tipos do que verá em um tutorial simples.