Menu

Tipos de dados em C#: int, double, decimal, char, bool e seus intervalos

Todos os tipos embutidos de C# com tamanho, intervalo e sufixo de literal, a diferença entre tipos de valor e de referência, por que decimal é o tipo para dinheiro, overflow de inteiros e checked, e valores padrão.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

C# tem tipagem estática: toda variável, campo e expressão tem um tipo conhecido em tempo de compilação. A linguagem tem um conjunto de tipos embutidos com palavras-chave próprias (int, double, string...). Cada palavra-chave é um apelido para um tipo do namespace System, então int e System.Int32 são o mesmo tipo, e string e System.String são o mesmo tipo.

Os tipos embutidos

TipoTipo .NETTamanhoIntervalo ou precisãoLiteral
boolBoolean1 bytetrue ou falsetrue
byteByte1 byte0 a 255200
sbyteSByte1 byte-128 a 127-5
shortInt162 bytes-32.768 a 32.7671200
ushortUInt162 bytes0 a 65.53560000
intInt324 bytes-2.147.483.648 a 2.147.483.64742
uintUInt324 bytes0 a 4.294.967.29542u
longInt648 bytescerca de ±9,2 × 10^1842L
ulongUInt648 bytes0 a cerca de 1,8 × 10^1942ul
floatSingle4 bytescerca de ±3,4 × 10^38, ~6 a 9 dígitos2.5f
doubleDouble8 bytescerca de ±1,8 × 10^308, ~15 a 17 dígitos2.5 ou 2.5d
decimalDecimal16 bytescerca de ±7,9 × 10^28, 28 a 29 dígitos2.5m
charChar2 bytesuma unidade de código UTF-16, U+0000 a U+FFFF'A'
stringStringreferênciatexto de qualquer tamanho, imutável"hello"
objectObjectreferênciaa base de todos os tiposnew object()

Um literal inteiro sem sufixo é um int, ou o primeiro entre uint, long e ulong que consiga guardá-lo se for grande demais para um int, e um literal com ponto decimal é um double. É por isso que float f = 2.5; e decimal d = 2.5; não compilam: o literal é um double, e não existe conversão implícita de double para float ou decimal. Adicione o sufixo: 2.5f, 2.5m.

Todo tipo numérico expõe seus limites como constantes:

Saída:

byte:  0 to 255
short: -32768 to 32767
int:   -2147483648 to 2147483647
long:  -9223372036854775808 to 9223372036854775807
decimal max: 79228162514264337593543950335
double max:  1.798E+308
sizes: int 4, long 8, double 8, char 2
255 161 3000000000

Na prática, a maior parte do código usa quatro desses: int para contagens e índices, long para IDs e totais grandes, double para medições e matemática, e decimal para dinheiro.

decimal vs double

double (e float) guardam números em binário. A maioria das frações decimais, incluindo 0.1, não tem forma binária exata, então elas são guardadas como o valor representável mais próximo e pequenos erros se acumulam. decimal guarda um inteiro na base 10 mais uma escala, então 0.1 é exatamente 0.1.

Saída:

False
True
0.30000000000000004
0.3
double sum == 1.0: False
decimal sum == 1.0: True
Total: 59.97
Split three ways: 19.99

Use decimal para dinheiro, preços, impostos e qualquer coisa que uma pessoa vá somar à mão e comparar. Ele mantém os zeros à direita das entradas (1.50m é impresso como 1.50) e tem de 28 a 29 dígitos significativos.

Use double para medições físicas, estatística, geometria e jogos. Ele é várias vezes mais rápido, usa metade da memória e tem um intervalo enorme. Nunca compare dois doubles calculados com ==; compare a diferença com uma tolerância: Math.Abs(x - y) < 1e-9.

decimal não é mágico: 1m / 3m continua sendo 0.3333333333333333333333333333, e a aritmética com double não lança exceção na divisão por zero (produz infinito, ou NaN para 0.0 / 0.0), enquanto a divisão por zero de decimal e de inteiros lança uma DivideByZeroException.

Overflow de inteiros e checked

Aritmética inteira que passa do intervalo do tipo dá a volta por padrão, sem aviso. O compilador pega o overflow em expressões constantes, mas não quando os valores vêm de variáveis:

Saída:

-2147483648
OverflowException
-1294967296
2147483648

checked(...) ou um bloco checked { ... } transforma o overflow em uma OverflowException. Você também pode ativar isso para o projeto inteiro com <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>. Escrever int x = int.MaxValue + 1; com constantes é um erro de compilação (CS0220), porque o compilador consegue ver o overflow.

A última linha mostra a solução para a maioria dos bugs de overflow: converter para um tipo maior antes da aritmética. (long)(max + 1) estaria errado, já que a soma já deu a volta quando o cast executa.

Tipos de valor e tipos de referência

Todo tipo em C# é um tipo de valor ou um tipo de referência, e a diferença decide o que a atribuição faz.

  • Tipos de valor: todos os tipos numéricos, bool, char, DateTime, enums e toda struct. A variável contém os dados; a atribuição os copia.
  • Tipos de referência: string, arrays, object e toda class. A variável contém uma referência para um objeto; a atribuição copia a referência, então as duas variáveis veem o mesmo objeto.

Saída:

a=10 b=99
first[0]=99
s1=cat s2=cats

string é um tipo de referência que, na prática, se comporta como um valor: ela é imutável, e == compara o texto, não as referências. Mais sobre tipos de valor definidos pelo usuário em structs.

char e bool

Um char é uma unidade de código UTF-16, escrita entre aspas simples. Por baixo, é um tipo numérico, então se converte para int e aceita aritmética:

Saída:

66
C
True
True
7
b
True

bool tem exatamente dois valores e não se converte de ou para números: if (count) é um erro de compilação, não um teste de diferente de zero. Escreva if (count != 0). Caracteres fora do Plano Multilíngue Básico, como a maioria dos emojis, ocupam dois valores char (um par substituto), então o Length de uma string conta unidades UTF-16, não caracteres visíveis.

Valores padrão

Campos, elementos de array e default(T) recebem o valor padrão do tipo: zero para números, false para bool, '\0' para char e null para tipos de referência. Variáveis locais não recebem padrão e precisam ser atribuídas antes do uso.

Saída:

0
0
False
True
0001-01-01
0,0,0

Desde o C# 7.1, você pode escrever o literal default sem o tipo quando o compilador consegue inferi-lo (int x = default;). Um tipo de valor não pode ser null, e é para isso que existe int? (um tipo de valor anulável); veja tipos anuláveis.

Inteiros do tamanho nativo e inteiros grandes

Mais dois tipos numéricos existem para casos especiais. nint e nuint (C# 9) são inteiros do tamanho de um ponteiro, 32 ou 64 bits conforme a plataforma, usados em interoperabilidade e código de baixo nível. System.Numerics.BigInteger guarda inteiros de qualquer tamanho, ao custo de velocidade:

using System.Numerics;
BigInteger factorial = 1;
for (int i = 2; i <= 30; i++) factorial *= i;
// 265252859812191058636308480000000

Perguntas frequentes

Quais são os tipos de dados em C#?

Os tipos embutidos são os inteiros (sbyte, byte, short, ushort, int, uint, long, ulong), os tipos de ponto flutuante float e double, decimal para aritmética decimal exata, char para um caractere UTF-16, bool, e os tipos de referência string e object. Cada palavra-chave é um apelido para um tipo do .NET: int é System.Int32, string é System.String.

Devo usar decimal ou double para dinheiro em C#?

Use decimal. Um double guarda frações binárias, então valores como 0.1 não podem ser representados exatamente e as somas se desviam (0.1 + 0.2 == 0.3 é false). Um decimal guarda dígitos na base 10, mantém de 28 a 29 dígitos significativos e dá os resultados que um contador espera. double é mais rápido e tem um intervalo muito maior, então use-o para ciência, gráficos e medições.

Qual é o valor máximo de int em C#?

int.MaxValue é 2.147.483.647 e int.MinValue é -2.147.483.648 (um inteiro de 32 bits com sinal). Somar 1 a int.MaxValue dá a volta sem aviso para int.MinValue, a menos que o código execute em um contexto checked, onde ele lança uma OverflowException. Use long (até cerca de 9,2 quintilhões) para valores maiores.

Qual a diferença entre float e double em C#?

float tem 32 bits e cerca de 6 a 9 dígitos significativos; double tem 64 bits e cerca de 15 a 17. Literais de ponto flutuante são double por padrão, então um float precisa do sufixo f: float speed = 2.5f;. Use double, a menos que a memória ou uma API (como o Vector3 da Unity) peça float.

Qual a diferença entre tipos de valor e tipos de referência em C#?

Uma variável de tipo de valor (int, double, bool, DateTime, qualquer struct) guarda os próprios dados, e a atribuição os copia. Uma variável de tipo de referência (string, arrays, qualquer class) guarda uma referência para um objeto no heap, e a atribuição copia a referência, então duas variáveis podem apontar para o mesmo objeto.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR