Por que existem tantos tipos numéricos (bit, int, float, double, long)?

9

Aprendi PHP, Java e C. Agora estou curioso para saber por que existem tantos tipos de dados numéricos como bit, int, float, double e long. Por que não criar apenas um tipo para números?

Existe algum benefício nisso? Talvez se usarmos números inteiros para armazenar números tão pequenos, podemos economizar memória?

GusDeCooL
fonte
6
Além da resposta do HorusKol: os tipos 'float' e 'integer' são inerentemente diferentes. Os flutuadores podem conter números muito grandes, mas à medida que o tamanho do número aumenta, a precisão diminui. Essa imprecisão deve-se à maneira como os carros alegóricos são armazenados. Por outro lado, o intervalo de valores que você pode armazenar em um número inteiro é bastante limitado, mas o valor é sempre exato, para que você possa comparar valores com muito mais facilidade. Além disso, existem dois tipos diferentes de comportamento com a divisão - números inteiros 'truncados' para o número inteiro mais próximo automaticamente, os flutuadores não. Cada um desses comportamentos é útil para diferentes situações.
Kampu
Javascript tem apenas um tipo de número na superfície.
Esailija 25/05
@ kampu: Na verdade, em muitos idiomas, os números inteiros podem armazenar qualquer número, desde que a memória (virtual) seja grande o suficiente para representá-la.
Jörg W Mittag
11
@ JörgWMittag: No entanto, esse questionador está claramente falando sobre linguagens estáticas, não linguagens dinâmicas como Python, por exemplo. O próprio CPython implementa o número inteiro de 'alcance ilimitado' como uma matriz de 32 bits, com o bit final em cada int usado para indicar se há mais bits por vir. Além disso, números inteiros podem armazenar apenas qualquer número inteiro . Isso significa que um float com armazenamento infinito pode armazenar valores com precisão (infinito aleph um), enquanto números inteiros podem armazenar valores apenas com precisão ( infinito aleph zero ).
Kampu 26/05
@ kampu: Como todos os números são representados por séries de bits, mesmo com armazenamento infinito, sempre haverá um mapeamento de um para um entre números de ponto flutuante e números inteiros. Então, eu não acho que um deles seja questionado.
Vem de

Respostas:

17

Há duas razões pelas quais você deve se preocupar com os diferentes tipos de dados numéricos.

1. Economizando memória

for(long k=0;k<=10;k++)
{
    //stuff
}

Por que usar um longo quando poderia facilmente ser um número inteiro ou mesmo um byte? Você realmente salvaria vários bytes de memória ao fazer isso.

2. Números de ponto flutuante e números inteiros são armazenados de forma diferente no computador

Suponha que temos o número 22 armazenado em um número inteiro. O computador armazena esse número na memória em binário como:

0000 0000 0000 0000 0000 0000 0001 0110

Se você não conhece o sistema de números binários, isso pode ser representado em notação científica como: 2 ^ 0 * 0 + 2 ^ 1 * 1 + 2 ^ 2 * 1 + 2 ^ 3 * 0 + 2 ^ 4 * 1 + 2 ^ 5 * 0 + ... + 2 ^ 30 * 0. O último bit pode ou não ser usado para indicar se o número é negativo (dependendo se o tipo de dados é assinado ou não).

Essencialmente, é apenas um somatório de 2 ^ (bit place) * value.

Isso muda quando você está se referindo a valores que envolvem um ponto decimal. Suponha que você tenha o número 3,75 em decimal. Isso é conhecido como 11.11 em binário. Podemos representar isso como uma notação científica como 2 ^ 1 * 1 + 2 ^ 0 * 1 + 2 ^ -1 * 1 + 2 ^ -2 * 1 ou, normalizada, como 1.111 * 2 ^ 2

O computador não pode armazenar isso no entanto: não possui um método explícito de expressar esse ponto binário (a versão do sistema de números binários do ponto decimal). O computador pode armazenar apenas 1 e 0. É aqui que o tipo de dados de ponto flutuante entra.

Supondo que sizeof (float) seja 4 bytes, você terá um total de 32 bits. O primeiro bit é atribuído ao "bit de sinal". Não há carros alegóricos ou duplos não assinados. Os próximos 8 bits são usados ​​para o "expoente" e os 23 bits finais são usados ​​como o "significando" (ou algumas vezes chamado de mantissa). Usando nosso exemplo de 3,75, nosso expoente seria 2 ^ 1 e nosso significando seria 1,111.

Se o primeiro bit for 1, o número é negativo. Se não, positivo. O expoente é modificado por algo chamado "o viés"; portanto, não podemos simplesmente armazenar "0000 0010" como expoente. O viés para um número de ponto flutuante de precisão única é 127 e o viés para uma precisão dupla (é aqui que o tipo de dados duplo recebe seu nome) é 1023. Os 23 bits finais são reservados para o significando. O significando são simplesmente os valores à DIREITA do nosso ponto binário.

Nosso expoente seria o viés (127) + expoente (1) ou representado em binário

1000 0000

Nosso significado seria:

111 0000 0000 0000 0000 0000

Portanto, 3,75 é representado como:

0100 0000 0111 0000 0000 0000 0000 0000

Agora, vejamos o número 8 representado como um número de ponto flutuante e como um número inteiro:

0100 0001 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 1000

Como no mundo o computador adicionará 8.0 e 8? Ou mesmo multiplicá-los !? O computador (mais especificamente, computadores x86) possui diferentes partes da CPU que adicionam números de ponto flutuante e números inteiros.

cpmjr123
fonte
3
3) embora raramente seja um problema: as operações em números maiores que o tamanho das palavras do computador são mais lentas.
Loren Pechtel
6

Antes de termos sistemas de gigabytes (ou em sistemas embarcados modernos como o Arduino), a memória era escassa e, portanto, os métodos abreviados foram implementados para especificar quanta memória um determinado número ocuparia - o BIT é direto - originalmente ocuparia apenas 1 bit. de memória.

Os outros tamanhos e nomes de dados variam entre os sistemas. Em um sistema de 32 bits, INT (ou MEDIUMINT) geralmente seria 2 bytes, LONGINT seria 4 bytes e SMALLINT seria um único byte. Os sistemas de 64 bits podem ter LONGINT definido em 8 bytes.

Mesmo agora - especialmente em aplicativos de bancos de dados ou programas que possuem várias instâncias em execução em servidores (como scripts do lado do servidor em sites) - você deve ter cuidado com o que escolher. Escolher um número inteiro de 2, 4 ou 8 bytes de largura para armazenar valores entre 0 e 100 (que pode caber em um byte) é incrivelmente inútil se você tiver uma tabela de banco de dados com milhões de registros.

Mais informações: https://en.wikipedia.org/wiki/Integer_(computer_science)

HorusKol
fonte
boa resposta +1.
Vinay
7
Não apenas 'antes', mas também 'agora quando um sistema é pequeno'. Em um dispositivo do tamanho de um Arduino, é necessário ser econômico.
9000
11
Qual sistema usou apenas 1 bit para armazenar um pouco? bits geralmente não são diretamente endereçáveis
jk.
11
isso é verdade em muitas arquiteturas - mas os bits eram diretamente endereçáveis ​​em sistemas realmente antigos e até mesmo em alguns sistemas embarcados mais recentes (alguns controladores que eu programava há apenas 10 anos trabalhavam com bits - esses tinham apenas 64 locais endereçáveis ​​de larguras específicas). Atualmente, acho que os compiladores resolvem e os colocam em matrizes de bytes.
HorusKol 25/05
Eu acho que o principal fator é a capacidade da CPU e desempenho, em vez de preocupações de memória
James
4

Além dos excelentes pontos de vista do cpmjr123 sobre escassez de memória, precisão e alcance de trocas, também é potencialmente uma troca de CPU.

A maioria das máquinas modernas possui hardware especial para executar operações de ponto flutuante chamado FPU. Também existem sistemas que não possuem FPUs (hoje em dia esses dispositivos são tipicamente pequenos), consequentemente, dependendo do hardware de destino, você não precisa usar tipos de ponto flutuante ou usar uma biblioteca de pontos flutuantes de software. Mesmo que sua máquina possua uma FPU, historicamente existem diferenças nas funções que ela pode oferecer. Quaisquer funções não executadas no hardware teriam que ser executadas no software (ou evitadas)

Os cálculos de ponto flutuante no software são realizados executando muitas operações mais simples, suportadas pelo hardware. Portanto, você também obtém uma troca potencial de velocidade.

jk.
fonte
4

Talvez o mais importante seja que haja realmente três tipos de números básicos diferentes.

inteiro, decimal fixo e ponto flutuante.

Todos eles se comportam de maneira diferente.

Uma operação simples como 7/2 pode fornecer respostas de 3, 3,50 e 3,499, dependendo do tipo de dados usado.

"decimal fixo" é do tipo Cinderela, só é suportado nativamente em alguns idiomas como COBOL e VisualBasic. É de pouco interesse para os cientistas da computação, mas é vital para quem envia um conjunto de contas ou calcula o imposto sobre vendas em uma fatura.

James Anderson
fonte
Eu os separaria de maneira diferente: números discretos, números aproximados e anéis algébricos. Exemplos típicos em C seria int, float, e unsigned int, respectivamente. Tipos de ponto fixo são uma subcategoria de tipos discretos, mas os anéis algébricos são fundamentalmente diferentes dos números [a confusão da respeito de tipos não assinados em C decorre do fato de que eles geralmente se comportam como anéis em vez de números, mas não são consistentes] .
Supercat
3

Existe algum benefício que eles fazem?

Claro. Há benefícios. No mundo dos computadores, a memória é uma das coisas mais importantes a considerar. Para que serve ter uma memória de 2kb quando os dados podem caber em menos de 1kb? . Otimizações devem estar lá. Se você usa mais memória, obviamente diminui a velocidade dos computadores em um ponto. Você realmente gosta de tê-lo? Não está certo ...?

int - 2 bytes (16 bits)

long - 4 bytes (32 bits)

long long - 8 bytes (64 bits)

float - 4 bytes

Não apenas a memória, mas também a organização do tipo de números. para um ponto flutuante da instância. A precisão é muito importante e, obviamente, devemos ter um tipo que possa nos dar mais precisão.

Se considerarmos os velhos tempos, tínhamos muito menos memória, como você deve saber. Para salvá-lo e usá-lo com sabedoria, tivemos essas diferenças. E muito mais, se você apenas seguir em frente e tentar pesquisar no Google. Espero que isso ajude.

Vinay
fonte
3

números inteiros e reais (float, double) são tipos conceitualmente diferentes, com diferentes conjuntos de operações e propriedades intrínsecas.

Os números inteiros são enumeráveis, mas os flutuadores não, etc.

De fato, número flutuante / duplo é uma estrutura que combina dois campos inteiros: mantissa e expoente. Números complexos (que você excluiu da consideração) são ainda mais complexos.

Qualquer linguagem prática deve ter pelo menos números inteiros e flutuantes como tipos distintos - operações muito diferentes.

sorriso
fonte
Não estou familiarizado com os "números complexos" que você mencionou. Você pode explicar mais?
Cpmjr123
verifique isto: en.wikipedia.org/wiki/Complex_number
c-smile
Estou ciente de números complexos na forma de um + bi. Eu estava pedindo mais informações sobre como o computador armazena números complexos. Que eu saiba, não há tipos de dados primitivos que suportem isso.
Cpmjr123
Números complexos são tipicamente armazenados como dois valores de ponto flutuante, a saber, a(parte real) e b(parte imaginária). A CPU normalmente não implementa suporte nativo para operações em números complexos, embora a CPU possa implementar instruções aceleradas de adição múltipla para operações em pares de valores, como (a b + c d) e (a b-c d).
rwong 27/05
11
Além disso, muitas linguagens têm alguns tipos cujo comportamento é amplamente definido como o de um anel algébrico envolvente (por exemplo, se uma variável do tipo uint16_tcontiver 65535, incrementá-la fará com que ela contenha 0). Idealmente, as linguagens teriam tipos separados de maneira limpa para representar números e anéis algébricos quebrados (permitindo que os números que transbordam sejam capturados, permitindo que o código execute operações facilmente nas coisas que se espera que sejam quebradas).
Supercat
-1

Além do fato de que os tipos de ponto flutuante se comportam completamente diferentes dos tipos inteiros, quero dar um exemplo mais extremo do porquê o tamanho por número realmente importa.

Imagine que você deseja classificar uma matriz (longa). Por exemplo em C:

int numbers[100000000];

Então aqui temos 100 milhões de números.

Se cada número tiver apenas um byte de comprimento (usando-o em unsigned charvez de int), será necessário 100 milhões de bytes de espaço.

Se você usar double, geralmente são 8 bytes por número, portanto, 800 milhões de bytes de espaço.

Portanto, toda vez que você opera com muitos objetos (números neste exemplo), o tamanho por objeto (tamanho por número neste exemplo) realmente importa.

Ingo Blackman
fonte