Se as máquinas de 32 bits podem lidar apenas com números de até 2 ^ 32, por que posso escrever 1000000000000 (trilhões) sem que minha máquina pare de funcionar?


369

Os computadores de 32 bits podem armazenar apenas números inteiros assinados até 2 31 - 1.
É por isso que ficamos sem endereços IPv4 e entramos na era de 64 bits.

No entanto, o número 2 31 - 1 (2.147.483.647) não é tão grande quanto o número 1 trilhão (1.000.000.000.000) que eu pareço capaz de exibir bem sem que minha máquina falhe.

Alguém pode explicar por que isso é?


35
A questão é falha. Máquinas de 32 bits podem manipular números muito maiores que 2 ^ 32. Eles fazem isso o tempo todo, com 'long' e assim por diante. Eles podem armazenar apenas 2 ^ 32 em um registro, mas o software foi escrito para contornar esse problema. Algumas línguas modernas nem sequer têm um problema com o tamanho de um determinado número.
JFA

23
Por favor, mantenha os comentários no tópico, educados e relevantes para os aspectos técnicos da pergunta. Quase 50 comentários de piadas já tiveram que ser removidos e gostaríamos de evitar o bloqueio da postagem. Obrigado.
Nhinkle

6
Esta pergunta foi escrita de uma maneira um pouco superficial. O que você quer dizer com "escrever" e "exibir" o número 1000000000000? Quando você escreveu a pergunta, você escreveu o número 1000000000000 e o seu navegador da Web exibe muito bem, presumo, mas isso não deve ser nada estranho para quem já usou um computador antes. A pergunta pede uma interpretação livre.
hellogoodbye

7
Estima-se que a consciência humana tenha cerca de 50 bits (eu li em algum lugar). Portanto, a pergunta não é "Como posso escrever 10^9sem meu PC travar?" mas sim "Como posso escrever 10^(18)sem meu cérebro bater?"
Hagen von Eitzen

1
Os computadores de 32 bits podem armazenar apenas números inteiros NÃO ASSINADOS até 2 ^ 32 - 1. 2 ^ 32 - 1 não é igual a 2.147.483.647 ... 300 votos positivos e ninguém percebeu isso?
Koray Tugay

Respostas:


784

Respondo sua pergunta, fazendo uma pergunta diferente:

Como você conta com os dedos para 6?

Você provavelmente conta até o maior número possível com uma mão e depois passa para a segunda mão quando fica sem dedos. Os computadores fazem o mesmo, se precisarem representar um valor maior do que um único registro pode conter, usarão vários blocos de 32 bits para trabalhar com os dados.


16
Engraçado, @codename. Como então você conta com seus dedos até 32 ou mais (ou seja, quando 2 ^ 5 se esgota)? ;) A analogia de passar para a outra mão é boa ... mesmo que o binário atrase a necessidade de passar para a outra mão. O que eu gostaria de ver é contar 1.024 ou mais com a destreza dos pedais para passar aos dedos dos pés para contar ainda mais em binário - até 1.048.575! :) Isso é potencialmente 20 bits de potência da placa-filha. : P
J0e3gan

14
Por favor, mantenha os comentários no tópico e relevantes para discutir os aspectos técnicos desta resposta. Mais de 60 comentários de piadas já foram excluídos desta resposta e gostaríamos de evitar o bloqueio da postagem.
Nhinkle

@ codename- easy, você atribui um dedo como ponteiro de pilha. Depois de ficar sem dedos, adicione o valor à pilha e reinicie a contagem.
Makach

Onde você aprendeu isso, @codename? Eu ouvi isso primeiro de Frederik Pohl, veja, por exemplo, aqui hjkeen.net/halqn/f_pohl3.htm
Zane

2
Eu acho que essa não é a resposta para a pergunta relevante. Responder por @ Bigbio2002 é o correto. Aqui "1000000000000" não é um número, mas um texto, assim como "adsfjhekgnoregrebgoregnkevnregj". O que você está dizendo é verdade, mas sinto fortemente que essa não é a resposta correta. E ver tantos upvotes ...
Master Chief

398

Você está certo de que um número inteiro de 32 bits não pode conter um valor maior que 2 ^ 32-1. No entanto, o valor desse número inteiro de 32 bits e como ele aparece na tela são duas coisas completamente diferentes. A sequência impressa "1000000000000" não é representada por um número inteiro de 32 bits na memória.

Para exibir literalmente o número "1000000000000", são necessários 13 bytes de memória. Cada byte individual pode conter um valor de até 255. Nenhum deles pode conter o valor numérico inteiro, mas interpretado individualmente como caracteres ASCII (por exemplo, o caractere ' 0' é representado pelo valor decimal 48, valor binário 00110000), eles podem ser unidos em um formato que faça sentido para você, um humano.


Um conceito relacionado na programação é typecasting , que é como um computador irá interpretar um fluxo específico de 0s e 1s. Como no exemplo acima, ele pode ser interpretado como um valor numérico, um caractere ou até mesmo algo completamente diferente. Embora um número inteiro de 32 bits não consiga manter um valor de 1000000000000, um número de ponto flutuante de 32 bits poderá, usando uma interpretação totalmente diferente.

Quanto à forma como os computadores podem trabalhar e processar grandes números internamente, existem números inteiros de 64 bits (que podem acomodar valores de até 16 bilhões de bilhões), valores de ponto flutuante e bibliotecas especializadas que podem trabalhar com dados arbitrariamente grandes. números.


22
Na verdade, isso é mais correto, mas não completamente. É improvável que um número de ponto flutuante de 32 pontos possa representar com precisão 1000000000000. Representará um número muito muito próximo do número desejado, mas não exatamente.
Tim B

6
@ TimB: Você já ouviu falar sobre o formato decimal32? Faz parte do padrão IEEE 754-2008. Este formato é capaz de representação correta desse número :)
VX

15
É verdade que pode. No entanto, esse não é o formato que as pessoas querem dizer quando dizem "flutuar", que geralmente se refere a um número de ponto flutuante de 32 bits como armazenado e usado pelos processadores padrão de ponto flutuante nos computadores atuais.
Tim B

2
@ TimB de fato. O número mais próximo ao que pode ser representado como um float32 é 999999995904
Greggo

4
@ TimB: Mas um número de ponto flutuante de 64 bits pode facilmente representar 1000000000000exatamente. É 10 ^ 12 ou 2 ^ 12 * 5 ^ 12; 5 ^ 12 requer 28 bits de mantissa.
Keith Thompson

191

Em primeiro lugar, os computadores de 32 bits podem armazenar números de até 2³²-1 em uma única palavra de máquina . Palavra de máquina é a quantidade de dados que a CPU pode processar de maneira natural (por exemplo, operações com dados desse tamanho são implementadas em hardware e geralmente são mais rápidas de executar). As CPUs de 32 bits usam palavras que consistem em 32 bits, portanto, podem armazenar números de 0 a 2³²-1 em uma palavra .

Segundo, 1 trilhão e 1000000000000 são duas coisas diferentes.

  • 1 trilhão é um conceito abstrato de número
  • 1000000000000 é texto

Pressionando 1uma vez e depois 012 vezes, você está digitando texto. 1entradas 1, 0entradas 0. Vejo? Você está digitando caracteres. Personagens não são números. As máquinas de escrever não tinham CPU nem memória e estavam lidando muito bem com esses "números", porque é apenas texto.

Prova de que 1000000000000 não é um número, mas texto: pode significar 1 trilhão (em decimal), 4096 (em binário) ou 281474976710656 (em hexadecimal). Tem ainda mais significados em diferentes sistemas. O significado de 1000000000000 é um número e armazená-lo é uma história diferente (retornaremos a ele em um momento).

Para armazenar o texto (na programação, é chamado string ) 1000000000000, você precisa de 14 bytes (um para cada caractere mais um byte NULL de terminação que basicamente significa "a string termina aqui"). São 4 palavras-máquina. 3 e metade seria suficiente, mas como eu disse, as operações com palavras de máquina são mais rápidas. Vamos supor que o ASCII seja usado para armazenamento de texto, portanto, na memória, será assim: (convertendo códigos ASCII correspondentes a 0e 1ao binário, cada palavra em uma linha separada)

00110001 00110000 00110000 00110000
00110000 00110000 00110000 00110000
00110000 00110000 00110000 00110000
00110000 00000000 00000000 00000000

Quatro caracteres se encaixam em uma palavra, o restante é movido para o próximo. O restante é movido para a próxima palavra até que tudo (incluindo o primeiro byte NULL) se encaixe.

Agora, voltando ao armazenamento de números. Funciona exatamente como o texto transbordando, mas eles são ajustados da direita para a esquerda. Pode parecer complicado, então aqui está um exemplo. Por uma questão de simplicidade, vamos supor que:

  • nosso computador imaginário usa decimal em vez de binário
  • um byte pode conter números 0..9
  • uma palavra consiste em dois bytes

Aqui está uma memória vazia de 2 palavras:

0 0
0 0

Vamos armazenar o número 4:

0 4
0 0

Agora vamos adicionar 9:

1 3
0 0

Observe que os dois operandos caberiam em um byte, mas não no resultado. Mas temos outro pronto para usar. Agora vamos armazenar 99:

9 9
0 0

Novamente, usamos o segundo byte para armazenar o número. Vamos adicionar 1:

0 0
0 0

Opa ... Isso é chamado de excesso de número inteiro e é uma causa de muitos problemas sérios, às vezes muito caros .

Mas se esperamos que o estouro aconteça, podemos fazer o seguinte:

0 0
9 9

E agora adicione 1:

0 1
0 0

Torna-se mais claro se você remover espaços e linhas de separação de bytes:

0099    | +1
0100

Previmos que o excesso pode ocorrer e podemos precisar de memória adicional. Manipular números dessa maneira não é tão rápido quanto com números que se encaixam em palavras únicas e precisa ser implementado em software. A adição de suporte para números de palavras de dois e 32 bits a uma CPU de 32 bits o torna efetivamente uma CPU de 64 bits (agora ele pode operar em números de 64 bits nativamente, certo?).

Tudo o que descrevi acima também se aplica à memória binária com bytes de 8 bits e palavras de 4 bytes, funciona da mesma maneira:

00000000 00000000 00000000 00000000 11111111 11111111 11111111 11111111    | +1
00000000 00000000 00000000 00000001 00000000 00000000 00000000 00000000

Converter esses números para o sistema decimal é complicado, no entanto. (mas funciona muito bem com hexadecimal )


21
Sua resposta é bastante condescendente. OP é claramente falar sobre o número, não o texto: large as the number 1 trillion (1000000000000). Além disso, você está quase falando bignum , mas você nunca mencionar qualquer um dos termos para o que você está dizendo ....
MirroredFate

12
"1 trilhão" também é uma string
Elzo Valugi

3
@ElzoValugi It is. Eu tive que encontrar uma maneira de apresentar o conceito de número abstrato, em vez de string representando um número. Eu acredito que "1 trilhão" é uma maneira melhor e menos ambígua de fazê-lo (veja a prova na resposta).
gronostaj

25
@MirroredFate Não concordo com 'está claramente falando sobre o número'. OP diz 'exibida fina', que claramente está falando sobre o texto '1000000000000' para mim ...
Joe

4
@yannbane 'A' é um caractere e não um número. '?' é um personagem e não um número. '1' é um caractere e não um número também. Personagens são apenas símbolos. Eles podem representar dígitos ou números, mas definitivamente não são números. '1' pode representar um, dez, cem, mil e assim por diante, é apenas um símbolo que representa um dígito que pode ser um número ou sua parte. '10' (sequência de caracteres) pode significar dois, oito, dez ou dezesseis etc., mas quando você diz que tem dez maçãs, está usando o número dez e todos sabem o que você quer dizer. Há uma enorme diferença entre caracteres e números.
gronostaj

40

Você também pode escrever "ESTA DECLARAÇÃO É FALSA" sem o computador travar :) @ A resposta de Scott é direta para certas estruturas de cálculo, mas sua pergunta de "escrever" um grande número implica que é apenas texto sem formatação, pelo menos até é interpretado.

Editar: agora, com menos sarcasmo, informações mais úteis sobre diferentes maneiras de armazenar um número na memória. Vou descrevê-los com maior abstração, ou seja, em termos em que um programador moderno pode estar escrevendo código antes de ser traduzido para código de máquina para execução.

Os dados em um computador precisam ser restritos a um determinado tipo , e uma definição desse tipo descreve quais operações podem ser executadas nesses dados e como (por exemplo, comparar números, concatenar texto ou XOR um booleano). Você não pode simplesmente adicionar texto a um número, assim como não pode multiplicar um número por texto, para que alguns desses valores possam ser convertidos entre tipos.

Vamos começar com números inteiros não assinados . Nestes tipos de valor, todos os bits são usados ​​para armazenar informações sobre dígitos; o seu é um exemplo de um número inteiro não assinado de 32 bits em que qualquer valor de 0até 2^32-1pode ser armazenado. E sim, dependendo do idioma ou arquitetura da plataforma usada, você pode ter números inteiros de 16 bits ou números inteiros de 256 bits.

E se você quiser ser negativo? Intuitivamente, números inteiros assinados é o nome do jogo. A convenção é alocar todos os valores de -2^(n-1)para 2^(n-1)-1- dessa forma, evitamos a confusão de ter que lidar com duas maneiras de escrever +0e -0. Portanto, um número inteiro assinado de 32 bits manteria um valor de -2147483648para 2147483647. Legal, não é?

Ok, cobrimos números inteiros que são números sem um componente decimal. Expressar isso é mais complicado: a parte não inteira pode sensivelmente estar apenas em algum lugar entre 0e 1, portanto, cada bit extra usado para descrevê-la aumentaria sua precisão: 1/2, 1/4, 1/8 ... O problema é que você Não é possível expressar com precisão um decimal simples 0.1como uma soma de frações que só podem ter potências de dois em seu denominador! Não seria muito mais fácil armazenar o número como um número inteiro, mas concordaria em colocar o ponto de raiz (decimal)? Isso é chamado de número de ponto fixo , onde armazenamos, 1234100mas concordamos em uma convenção de lê-lo como 1234.100alternativa.

Um tipo relativamente mais comum usado para cálculos é floating point. O modo como ele funciona é realmente elegante, ele usa um bit para armazenar o valor do sinal e outros para armazenar o expoente e o significando. Existem padrões que definem essas alocações, mas para um flutuador de 32 bits, o número máximo que você seria capaz de armazenar é um número impressionante

(2 - 2^-23) * 2^(2^7 - 1) ≈ 3.4 * 10^38

No entanto, isso tem um custo de precisão. O JavaScript disponível nos navegadores usa flutuadores de 64 bits e ainda não consegue fazer as coisas direito. Basta copiar isso na barra de endereços e pressionar enter. Alerta de spoiler: o resultado não será 0.3.

javascript:alert(0.1+0.2);

Existem mais tipos alternativos, como o Microsoft .NET 4.5 BigInteger, que teoricamente não tem limites superiores ou inferiores e deve ser calculado em "lotes"; mas talvez as tecnologias mais fascinantes sejam as que entendem matemática, como o mecanismo Wolfram Mathematica, que pode trabalhar com precisão com valores abstratos como o infinito .


8
Você pode fazer isso nesta realidade. Tente fazer isso no universo de Star Trek. Afaste-se antes, por causa de todas as faíscas e fumaça.
quer

Não é exatamente assim que o ponto fixo funciona. Na verdade, é um sistema em que os números são redimensionados e tendenciosos para produzir o ponto decimal. No seu exemplo, a escala é 1/1000, mas também existem números de ponto fixo (especialmente em computação gráfica) assim: 0 = 0,0, 255 = 1,0 - a escala é 1/255.
Andon M. Coleman

31

A chave é entender como os computadores codificam números.

É verdade que, se um computador insistir em armazenar números usando uma representação binária simples do número usando uma única palavra (4 bytes em um sistema de 32 bits), um computador de 32 bits poderá armazenar apenas números de até 2 ^ 32. Mas existem muitas outras maneiras de codificar números, dependendo do que você deseja obter com eles.

Um exemplo é como os computadores armazenam números de ponto flutuante. Os computadores podem usar várias maneiras diferentes de codificá-los. O padrão IEEE 754 define regras para codificar números maiores que 2 ^ 32. De maneira grosseira, os computadores podem implementar isso dividindo os 32 bits em partes diferentes, representando alguns dígitos do número e outros bits representando o tamanho do número (ou seja, o expoente, 10 ^ x). Isso permite uma faixa muito maiorde números em termos de tamanho, mas compromete a precisão (o que é bom para muitos propósitos). Obviamente, o computador também pode usar mais de uma palavra para essa codificação, aumentando a precisão da magnitude dos números codificados disponíveis. A versão decimal simples 32 do padrão IEEE permite números com cerca de 7 dígitos decimais de precisão e números de até 10 ^ 96 em magnitude.

Mas existem muitas outras opções se você precisar de precisão extra. Obviamente, você pode usar mais palavras na sua codificação sem limite (embora com uma penalidade de desempenho para converter para dentro e para fora do formato codificado). Se você deseja explorar uma maneira de fazer isso, existe um ótimo suplemento de código-fonte aberto para Excel que usa um esquema de codificação que permite centenas de dígitos de precisão no cálculo. O suplemento é chamado Xnumbers e está disponível aqui . O código está no Visual Basic, que não é o mais rápido possível, mas tem a vantagem de ser fácil de entender e modificar. É uma ótima maneira de aprender como os computadores conseguem codificar números maiores. E você pode brincar com os resultados no Excel sem precisar instalar nenhuma ferramenta de programação.


24

Está tudo na sua pergunta.

Você pode escrever qualquer número que desejar no papel. Tente escrever um trilhão de pontos em uma folha de papel branca. É lento e ineficaz. É por isso que temos um sistema de 10 dígitos para representar esses grandes números. Temos até nomes para grandes números como "milhões", "trilhões" e mais, para que você não diga one one one one one one one one one one one...em voz alta.

Os processadores de 32 bits foram projetados para funcionar de maneira mais rápida e eficiente com blocos de memória com exatamente 32 dígitos binários. Mas nós, pessoas, geralmente usamos sistema numérico de 10 dígitos e computadores, sendo eletrônicos, usamos sistema de 2 dígitos ( binário ). Os números 32 e 64 são potências de 2. Assim, um milhão e um trilhão são potências de 10. É mais fácil operar com esses números do que multidões de 65536, por exemplo.

Dividimos grandes números em dígitos quando os escrevemos no papel. Os computadores dividem os números em um número maior de dígitos. Podemos escrever qualquer número que quisermos, e os computadores também, se os projetarmos.


15

32 bits e 64 bits referem-se a endereços de memória. A memória do computador é como caixas postais, cada uma com um endereço diferente. A CPU (Unidade Central de Processamento) usa esses endereços para endereçar os locais de memória na sua RAM (Memória de Acesso Aleatório). Quando a CPU suporta apenas endereços de 16 bits, você pode usar apenas 32 MB de RAM (o que parecia enorme na época). Com 32 bits, foi para 4 + gb (o que parecia enorme na época). Agora que temos endereços de 64 bits, a RAM entra em terabytes (o que parece enorme).
No entanto, o programa é capaz de alocar vários blocos de memória para armazenar números e texto, isso depende do programa e não está relacionado ao tamanho de cada endereço. Para que um programa possa informar à CPU, usarei 10 blocos de endereços de armazenamento e armazenarei um número muito grande, uma sequência de 10 letras ou qualquer outra coisa.
Nota lateral: Os endereços de memória são apontados por "ponteiros"; portanto, o valor de 32 e 64 bits significa o tamanho do ponteiro usado para acessar a memória.


2
Boa resposta, exceto pelos detalhes - 16 bits de espaço de endereço forneceram 64kb, não 32mb, e máquinas como a 286 tinham endereços de 24 bits (para 16mb). Além disso, com endereços de 64 bits, você vai muito além dos terabytes - mais como 16 exabytes - terabytes tem o tipo de limites que as placas-mãe / CPUs da geração atual estão impondo - e não o tamanho dos endereços.
Phil

4
32 bits refere-se ao tamanho da palavra da máquina, não aos endereços de memória. Como Phil mencionou, 286 era uma CPU de 16 bits, mas usava 24 bits para endereçar através da segmentação de memória. As CPUs x86 são de 32 bits, mas usam o endereçamento de 36 bits. Veja PAE .
gronostaj

O @gronostaj poço x86 possui endereçamento de 32 bits, do 386 ao Pentium.
Ruslan

Voto positivo porque esta é a única resposta CORRETA aqui - 32 bits refere-se ao endereçamento de memória de 32 bits, não à aritmética de 32 bits.
precisa saber é o seguinte

@ user1207217: ?? Então, de acordo com o seu raciocínio, por exemplo, o Z80 ou o 8080 são processadores de 16 bits (devido ao endereçamento de memória de 16 bits e ao barramento de memória)?
pabouk

13

Como a exibição do número é feita usando caracteres individuais, não números inteiros. Cada dígito no número é representado com um literal de caractere separado, cujo valor inteiro é definido pela codificação usada, por exemplo, 'a'é representado com valor ascii 97, enquanto '1'é representado por 49. Verifique a tabela ascii aqui .
Para exibir 'a' e '1' é o mesmo. Eles são literais de caracteres, não inteiros. É permitido que cada literal de caractere tenha um valor máximo de 255 na plataforma de 32 bits, armazenando o valor no tamanho de 8 bits ou 1 byte (isso depende da plataforma, porém 8 bits é o tamanho de caractere mais comum), portanto, eles podem ser agrupados e podem ser exibido. A quantidade de caracteres separados que eles podem exibir depende da RAM que você possui. Se você tiver apenas 1 byte de RAM, poderá exibir apenas um caractere; se tiver 1 GB de RAM, poderá exibir bem 1024 * 1024 * 1024 caracteres (com preguiça de fazer as contas).

Essa limitação, no entanto, se aplica aos cálculos, no entanto, acho que você está interessado no padrão IPV4. Embora não esteja totalmente relacionado aosbit-size, de alguma forma, afetou os padrões. Quando o padrão IPV4 foi criado, eles armazenaram os valores de ip em números inteiros de 32 bits. Agora, uma vez que você deu o tamanho, tornou-se padrão. Tudo o que sabemos sobre internet dependia disso e, em seguida, ficamos sem endereços IP para atribuir. Portanto, se o padrão IP foi revisado para ter 64 bits, tudo simplesmente parou de funcionar, incluindo o seu roteador (presumo que esteja correto) e outros dispositivos de rede. Portanto, um novo padrão deve ser criado, que apenas trocou o número inteiro de 32 bits pelo número um de 128 bits. E resto ajustado do padrão. O fabricante do hardware só precisa declarar que suporta esse novo padrão e ele se torna viral. Embora não seja tão simples, mas acho que você entendeu o ponto aqui.

Isenção de responsabilidade: a maioria dos pontos mencionados aqui é fiel à minha suposição. Talvez eu tenha perdido pontos importantes aqui para simplificar. Não sou bom em números, por isso devo ter perdido alguns dígitos, mas meu objetivo aqui é responder à resposta do OP sobre por que ele não trava o PC.


2
Não diminuí a votação, mas há vários problemas com sua resposta. 1é 0x31 em ASCII, não 0x1. 1 GB = 1024 ^ 3 B. O wad IPv4 inventado antes da introdução de CPUs de 32 bits, portanto, dizer que os endereços foram armazenados em números inteiros de 32 bits está em conflito com a pergunta do OP. E, finalmente, o IPv6 está usando endereços de 128 bits, não de 64 bits.
gronostaj

13

Nos processadores, há "palavras". Há palavras diferentes. Quando as pessoas dizem "processador de 32 bits", elas significam principalmente "largura do barramento de memória". Esta palavra consiste em diferentes "campos", que se referem a subsistemas de computador correspondentes a transmissão (24 bits) e controle (outros bits). Eu posso estar errado sobre números exatos, certifique-se disso através de manuais.

Um aspecto completamente diferente é a computação. Os conjuntos de instruções SSE e MMX podem armazenar números inteiros longos. O comprimento máximo sem perda de produtividade depende da versão atual do SSE, mas sempre tem múltiplos de 64 bits.

Os atuais processadores Opteron podem lidar com números de 256 bits (não tenho certeza sobre o número inteiro, mas o float é certo).

Resumo : (1) a largura do barramento não está conectada diretamente à largura do cálculo, (2) até mesmo palavras diferentes (palavra de memória, palavra de registro, palavra de barramento etc.) não se conectam entre si; caso contrário, elas têm um divisor comum entre 8 ou 16 ou 24. Muitos processadores até usaram palavras de 6 bits (mas sua história).


Não é verdade, o processador Pentium original tinha um barramento de dados de 64 bits para alta largura de banda de memória, mesmo sendo um processador de 32 bits. O 8088 era um processador de 16 bits com um barramento de dados de 8 bits.
precisa saber é o seguinte

10

O objetivo de um dispositivo de computação, geralmente, é aceitar, processar, armazenar e emitir dados. O hardware subjacente é apenas uma máquina que ajuda a executar essas quatro funções. Não pode fazer nenhum desses sem software.

Software é o código que informa à máquina como aceitar dados, como processá-los, como armazená-los e como fornecê-los a outras pessoas.

O hardware subjacente sempre terá limitações. No caso de uma máquina de 32 bits, a maioria dos registros que processam dados tem apenas 32 bits de largura. Isso não significa, porém, que a máquina não possa manipular números além de 2 ^ 32, significa que, se você deseja lidar com números maiores, pode levar mais de um ciclo para aceitá-la, processá-la, armazenar ou emita.

O software informa à máquina como lidar com números. Se o software foi projetado para lidar com números grandes, ele envia uma série de instruções para a CPU, que informam como lidar com números maiores. Por exemplo, seu número pode ser representado por dois registradores de 32 bits. Se você quisesse adicionar 1.234 ao seu número, o software instruiria a CPU a adicionar primeiro 1.234 ao registro inferior e depois verificaria o bit de estouro para ver se essa adição resultou em um número muito grande para o registro inferior. Caso isso aconteça, ele adiciona 1 ao registro superior.

Da mesma forma que as crianças em idade escolar são ensinadas a adicionar com carry, a CPU pode ser instruída a lidar com números maiores do que pode conter em um único registro. Isso é verdade para a maioria das operações matemáticas genéricas, para números de qualquer tamanho prático.


10

A diferença está em como armazenamos dados em computadores.

Você está certo de que, para uma máquina teórica de 8 bits, só podemos armazenar 2 ^ 8 valores em um único registro de processador ou endereço de memória. (Lembre-se de que isso varia de "máquina" para "máquina" com base no processador usado, na arquitetura da memória etc. Mas, por enquanto, vamos nos ater a uma máquina hipotética de 'estereótipo'.)

Para uma máquina teórica de 16 bits, o valor máximo em um local de registro / memória seria 2 ^ 16, para uma máquina de 32 bits, 2 ^ 32 etc.

Ao longo dos anos, os programadores criaram todo tipo de trapaça para armazenar e manipular números maiores do que os armazenados em um único registro de processador ou endereço de memória. Existem muitos métodos, mas todos envolvem o uso de mais de um endereço de registro / memória para armazenar valores maiores que a largura do local de registro / memória "nativo".

Todos esses métodos são benéficos, pois a máquina pode armazenar / processar valores maiores que sua capacidade nativa. A desvantagem é que quase todas as abordagens exigem várias instruções / leituras / etc. para lidar com esses números. Para o grande número ocasional, isso não é um problema. Ao lidar com muitos números grandes (principalmente endereços de memória grandes), a sobrecarga envolvida torna as coisas mais lentas.

Daí o desejo geral de tornar os registros, locais de memória e hardware de endereço de memória "cada vez mais amplos", para lidar com grandes números "de maneira nativa", para que esses números possam ser manipulados com o número mínimo de operações.

Como o tamanho do número é infinito, o registro do processador / tamanho da memória / endereçamento é sempre um equilíbrio entre o tamanho do número nativo e os custos envolvidos na implementação de larguras cada vez maiores.


8

Os computadores de 32 bits podem armazenar apenas números de até 2 ^ 32 em uma única palavra de máquina, mas isso não significa que eles não possam lidar com entidades maiores de dados.

O significado de um computador de 32 bits é geralmente que o barramento de dados e o barramento de endereços têm 32 bits de largura, o que significa que o computador pode lidar com 4 GB de espaço de endereço de memória de uma só vez e enviar quatro bytes de dados por vez no barramento de dados. .

Isso, no entanto, não limita o computador a lidar com mais dados, apenas precisa dividir os dados em blocos de quatro bytes quando é enviado pelo barramento de dados.

O processador Intel de 32 bits comum pode lidar com números de 128 bits internamente, o que permitiria lidar com números como 1000000000000000000000000000000000000000000 sem nenhum problema.

Você pode lidar com números muito maiores do que os de um computador, mas os cálculos precisam ser feitos por software, a CPU não possui instruções para lidar com números maiores que 128 bits. (Ele pode lidar com números muito maiores na forma de números de ponto flutuante, mas você só tem 15 dígitos de precisão.)


6

Apenas adicionando uma nota às muitas outras respostas, porque esse é um fato muito importante nesta questão que foi esquecida.

"32 bits" refere-se à largura do endereço de memória. Não tem nada a ver com o tamanho do registro. Muitas CPUs de 32 bits provavelmente possuem registradores de 64 ou até 128 bits. Em particular, referindo-se à linha de produtos x86, as CPUs de consumidor recentes, todas de 64 bits, possuem registros de até 256 bits para fins especiais.

Essa diferença entre a largura do registro e a largura do endereço existe desde os tempos antigos, quando tínhamos registradores de 4 bits e endereços de 8 bits, ou vice-versa.

É simples ver que armazenar um número grande não é problema, independentemente do tamanho do registro, conforme explicado em outras respostas.

A razão pela qual os registros, de qualquer tamanho que eles possam ter, também pode ser calculado com números maiores, é que cálculos muito grandes podem ser divididos em vários menores que se encaixam nos registros (é um pouco mais complicado na realidade).


Isso não é verdade; o que se refere a 64 bits é inconsistente, mas sistemas com larguras de registro de 64 bits são freqüentemente chamados de 64 bits. A Wikipedia diz que "uma arquitetura de computador de 64 bits geralmente possui registros de endereços e endereços com 64 bits de largura". Sim, a moderna linha de produtos x86 (ou AMD-64) possui enormes registros para fins especiais, mas eles têm registros principais de 64 bits e podem acessar 48-52 bits de memória; os sistemas x86 mais antigos têm registradores principais de 32 bits e acessam 24-36 bits de memória, e o 8086 era chamado de chip de 16 bits, possuía registradores de 16 bits de largura e acessava 20 bits de memória.
prosfilaes

@prosfilaes Isso é muita informação valiosa, eu estava me referindo a elas (não lembrei dos detalhes, assim como você). Sinta-se livre para editar isso na resposta.
Mafu

6

As respostas já dadas são realmente muito boas, mas tendem a abordar a questão de lados diferentes e, portanto, apresentam uma imagem incompleta. Eles também são um pouco excessivamente técnicos, na minha opinião.

Então, apenas para esclarecer algo que é sugerido, mas não expresso explicitamente em nenhuma das outras respostas, e que eu acho que é o cerne da questão:

Você está misturando vários conceitos em sua pergunta , e um deles ("32 bits") pode realmente se referir a uma variedade de coisas diferentes (e respostas diferentes assumiram diferentes interpretações). Todos esses conceitos têm algo a ver com o número de bits (1 e 0) usados ​​(ou disponíveis) em vários contextos de computação (o que eu quero dizer com isso será esclarecido pelos exemplos abaixo), mas os conceitos não são relacionados .

Explicitamente:

  • "IPv4 / 6" refere-se ao protocolo da Internet , um conjunto de regras que define como as informações devem ser empacotadas e interpretadas na Internet. A principal distinção (ou pelo menos a mais conhecida) entre IPv4 e IPv6 é que o espaço de endereço (ou seja, o conjunto de endereços que pode ser usado para distinguir entre diferentes locais da rede) é maior no IPv6. Isso tem a ver com a quantidade de bits em cada pacote de dados enviados pela rede que são alocados para (ou seja, reservados para a finalidade de) identificar o remetente do pacote e o destinatário pretendido.
    • Analogia não computacional: cada pacote é como uma carta enviada por correio tradicional e o espaço de endereço é como a quantidade de caracteres que você "pode" usar ao escrever o endereço e o endereço de retorno no envelope.
    • Não vejo isso mencionado em nenhuma das outras respostas até agora.
  • As "palavras" da memória do computador (32 e 64 bits) geralmente podem ser consideradas os menores dados que um computador usa ou "pensa". Esses bits de dados se reúnem para formar outros bits de dados , como pedaços de texto ou números inteiros maiores.
  • Os ponteiros de 32 bits podem ou não ser palavras, mas, no entanto, são tratados atomicamente (ou seja, como unidades individuais que não podem ser divididas em componentes menores). Os ponteiros são a maneira de nível mais baixo em que um computador pode gravar o local na memória de algum pedaço arbitrário de dados. Observe que o tamanho do ponteiro usado pelo computador (ou, na verdade, pelo sistema operacional) limita o intervalo de memória que pode ser acessado por um único ponteiro, uma vez que existem apenas o número possível de locais de memória que um ponteiro pode "apontar" para pois existem valores possíveis para o ponteiro em si. Isso é análogo ao modo como o IPv4 limita o intervalo de endereços possíveis da Internet, mas nãolimitar a quantidade de dados que podem estar presentes, por exemplo, em uma página da web específica. No entanto, o tamanho do ponteiro não limita o tamanho dos dados para os quais o ponteiro pode apontar. (Para um exemplo de um esquema para permitir que o tamanho dos dados exceda o intervalo do ponteiro, consulte a estrutura do ponteiro de inode do Linux . Observe que esse é um uso ligeiramente diferente da palavra "ponteiro" do que é típico, pois o ponteiro geralmente se refere a um ponteiro no memória de acesso aleatório, não espaço no disco rígido.)
    • Analogia não computacional: hmmmm .... esse é um pouco complicado. Talvez o sistema decimal de Dewey para indexar materiais da biblioteca seja um pouco semelhante? Ou qualquer sistema de indexação, realmente.
    • Veja a resposta do SiteNook .
    • Observe que minha explicação sobre os ponteiros acima elisa alguns detalhes sutis e, sem dúvida, não está totalmente correta. No entanto, nas linguagens de programação nas quais os programadores trabalham diretamente com ponteiros, o modo mental que desenhei é geralmente suficiente para fins práticos.
  • Os números que um computador pode "exibir" não são (para fins práticos) limitados pelo hardware ou sistema operacional do computador; eles são tratados como qualquer outro texto.

Observe que isso não pretende ser uma lista abrangente de interpretações para a frase "32 bits".

Crédito extra: para realmente ver a distinção filosófica básica entre números e partes primitivas da memória do computador, leia um pouco sobre as máquinas de Turing .


Acho que a referência ao IPv4 foi apontar que o número de endereços IPv4 é efetivamente limitado ao comprimento de um número inteiro de 32 bits assinado, enquanto o IPv6 usa 128 bits e, portanto, pode ter muitos pedidos de magnitude a mais.
Clonkex

@ Clonkex Possivelmente, embora essa definitivamente não seja a questão.
Kyle Strand

5

Se você escrever 1000000000000, por exemplo, na calculadora, o computador o calculará como um número de tipo Real com ponto decimal . O limite para 32 bits que você mencionou toca mais em todos os números do tipo Inteiro sem ponto decimal. Tipos de dados diferentes usam métodos diferentes para obter bits / bytes.

Números de tipo inteiro : esta tabela pode ajudá-lo a entender o ponto ( http://msdn.microsoft.com/en-us/library/296az74e.aspx ). Isso toca nos limites do C ++. Por exemplo, o número do tipo Int64 possui limites de -9223372036854775808 a 9223372036854775807.

Números de tipo real : números de tipo real contêm valor com ponto flutuante e expoente e você pode inserir números muito maiores, mas com exatidão / precisão limitadas. ( http://msdn.microsoft.com/en-us/library/6bs3y5ya.aspx ) Por exemplo, LDBL (duplo grande) em C ++ tem o expoente máximo 308, portanto, é possível inserir ou ter como resultado um número 9.999 x 10^308, significa que você teoricamente, 308 (+1) dígitos de 9mas apenas 15 dígitos mais importantes serão usados ​​para representá-lo, o resto será perdido, causa de precisão limitada.

Além disso, existem diferentes linguagens de programação e elas podem ter implementações diferentes de limites numéricos. Portanto, você pode imaginar que aplicativos especializados poderiam lidar com números muito maiores (e / ou mais exatos / precisos) que o C ++.


Esta "resposta" está incorreta: as calculadoras usam a representação do número BCD para evitar erros de truncamento. O IE 0.1 decimal não pode ser representado com precisão como um número binário de comprimento finito.
sawdust

5

Caso você queira um exemplo prático de quantos programas em um sistema Linux típico processam um grande número de processamento e saída:

libgmp- A Biblioteca Aritmética de Precisão Múltipla GNU é a biblioteca mais usada para esse fim em sistemas Linux. Um exemplo simples de multiplicar 2 ^ 80 por 1000:

#include <gmp.h>

// Each large integer uses the mpz_t type provided by libgmp
mpz_t a_large_number;
mpz_t base;
mpz_t result;

// Initalize each variable
mpz_init(a_large_number);
mpz_init(base);
mpz_init(result);

// Assign the number 2 to the variable |base|
mpz_set_ui(base, 2);

// Raise base^80 (2^80), store the result in |a_large_number|
mpz_pow_ui(a_large_number, base, 80);

// Multiply |a_large_number| by 1000, store the result in |result|
mpz_mul_ui(result, a_large_number, 1000);

// Finally, output the result in decimal and hex notation
gmp_printf("decimal: %Zd, hex: %ZX\n", result, result);

Então, basicamente, é o mesmo que usar os operadores + - * / normais, apenas com uma biblioteca para dividir os números e armazená-los internamente como números múltiplos de tamanho de palavra de máquina (ou seja, 32 bits). Também existem funções do tipo scanf () para manipular a conversão de entrada de texto em tipos inteiros.

A estrutura de mpz_té exatamente como o exemplo de Scott Chamberlain de contar até 6 usando as duas mãos. É basicamente uma matriz de mp_limb_ttipos de tamanho de palavra de máquina e, quando um número é muito grande para caber em uma palavra de máquina, o GMP usa vários mp_limb_tpara armazenar as partes alta / baixa do número.


5

Em sua mente, você conhece apenas 10 dígitos diferentes. 0 a 9. Internamente no seu cérebro, isso certamente é codificado de forma diferente do que no computador.

Um computador usa bits para codificar números, mas isso não é importante. Foi assim que os engenheiros escolheram codificar as coisas, mas você deve ignorá-las. Você pode pensar nisso como um computador de 32 bits tem uma representação única de mais de 4 bilhões de valores diferentes, enquanto nós, humanos, temos uma representação única para 10 valores diferentes.

Sempre que precisamos compreender um número maior, usamos um sistema. O número mais à esquerda é o mais importante. É 10 vezes mais importante que o próximo.

Um computador capaz de diferenciar entre quatro bilhões de valores diferentes também terá que fazer com que o valor mais à esquerda, em um conjunto de valores, seja quatro bilhões de vezes mais importante que o próximo valor nesse conjunto. Na verdade, um computador não se importa. Não atribui "importância" aos números. Os programadores devem criar código especial para cuidar disso.

Sempre que um valor se torna maior que o número de símbolos únicos, 9 na mente dos seres humanos, você adiciona um ao número à esquerda.

3+3=6

Nesse caso, o número ainda cabe em um único "slot"

5+5=10. This situation is called an overflow.

Portanto, os humanos sempre lidam com o problema de não ter símbolos únicos suficientes. A menos que o computador tenha um sistema para lidar com isso, ele simplesmente escreveria 0, esquecendo que havia um número extra. Felizmente, os computadores têm um "sinalizador de estouro" que é gerado nesse caso.

987+321 is more difficult.

Você pode ter aprendido um método na escola. Um algoritmo O algoritmo é bastante simples. Comece adicionando os dois símbolos mais à esquerda.

7+1=8, we now have ...8 as the result so far

Então você passa para o próximo slot e executa a mesma adição.

8+2=10, the overflow flag is raised. We now have ...08, plus overflow.

Como tivemos um estouro, isso significa que precisamos adicionar 1 ao próximo número.

9+3=12, and then we add one due to overflow. ...308, and we had another overflow.

Como não há mais números a serem adicionados, simplesmente criamos um slot e inserimos 1 porque o sinalizador de estouro foi aumentado.

1308

Um computador faz exatamente da mesma maneira, exceto que possui 2 ^ 32 ou até 2 ^ 64 símbolos diferentes, em vez de apenas 10 como seres humanos.

No nível do hardware, o computador trabalha em bits únicos usando exatamente o mesmo método. Felizmente, isso é abstraído para os programadores. Bits tem apenas dois dígitos, porque é fácil de representar em uma linha de energia. A luz está acesa ou apagada.

Finalmente, um computador pode exibir qualquer número como uma simples sequência de caracteres. É nisso que os computadores são melhores. O algoritmo para converter entre uma sequência de caracteres e uma representação interna é bastante complexo.


Na minha cabeça, conheço 36, mas normalmente uso apenas 16 deles.
Kyle Strand

"Um computador usa bits para codificar números, mas isso não é importante." No contexto do usuário, é muito importante perguntar sobre palavras de 32 bits e como elas são usadas para armazenar números maiores que 2 ^ 32-1.
precisa saber é o seguinte

Não é importante como você codifica números na memória do seu cérebro. Você tem um número finito de representações; a maioria aprendeu 10 símbolos diferentes. Dentro do seu cérebro, isso provavelmente é representado na forma de milhares de neurônios e sinapses. Em um computador, ele é representado na forma de eletricidade ou sem eletricidade em uma linha de energia. Do ponto de vista da programação - ou ao aprender matemática, isso não é importante, exceto nos raros casos em que você está programando diretamente para um conjunto específico de CPUs. Ele está perguntando sobre 32 bits vs 64 bits, não bits individuais.
Frodeborli

3

Porque você não está exibindo um número (no que diz respeito ao computador), mas uma string ou uma sequência de dígitos. Claro, alguns aplicativos (como a calculadora, eu acho), que lidam com números, podem lidar com esse número, eu acho. Não sei que truques eles usam ... Tenho certeza que algumas das outras respostas mais elaboradas cobrem isso.


0

A maior parte do conteúdo desta resposta veio originalmente dessa resposta (escrita antes dessa outra pergunta ter sido marcada como duplicada). Portanto, discuto o uso de valores de 8 bits (embora essa pergunta tenha sido feita sobre valores de 32 bits), mas tudo bem, porque os valores de 8 bits são mais simples de entender conceitualmente, e os mesmos conceitos se aplicam a valores maiores, como a aritmética de 32 bits.

Quando você adiciona dois números de 8 bits, o maior número possível (0xFF + 0xFF = 1FE). De fato, se você multiplicar dois números de 8 bits, o maior número possível (0xFF * 0xFF = 0xFE01) ainda será de 16 bits, duas vezes de 8 bits.

Agora, você pode estar assumindo que um processador x-bit pode acompanhar apenas x-bits. (Por exemplo, um processador de 8 bits pode acompanhar apenas 8 bits.) Isso não é exato. O processador de 8 bits recebe dados em pedaços de 8 bits. (Esses "pedaços" geralmente têm um termo formal: uma "palavra". Em um processador de 8 bits, são usadas palavras de 8 bits. Em um processador de 64 bits, podem ser usadas palavras de 64 bits.)

Portanto, quando você fornece 3 bytes ao computador:
Byte # 1: A instrução MUL
Byte # 2: os bytes de ordem superior (por exemplo, 0xA5)
Byte # 3: os bytes de ordem inferior (por exemplo, 0xCB)
O computador pode gerar um resultado que é mais do que 8 bits. A CPU pode gerar resultados como este:
0100 0000 0100 0010 xxxx xxxx xxxx xxxx 1101 0111
aka:
0x4082xxxxD7
Agora, deixe-me interpretar isso para você:
0x significa apenas que os seguintes dígitos são hexadecimais.
Discutirei momentaneamente o "40".
82 faz parte do registro "A", que é uma série de 8 bits.
xx e xx fazem parte de outros dois registradores, denominados registrador "B" e registrador "C". O motivo pelo qual não preenchi esses bits com zeros ou uns é que uma instrução "ADD" (enviada para a CPU) pode resultar na alteração desses bits pela instrução (enquanto a maioria dos outros bits que eu uso neste exemplo pode seja alterado, exceto por alguns dos bits da flag).
D7 caberia em mais bits, chamado de registro "D".
Um registro é apenas um pedaço de memória. Os registradores são incorporados às CPUs, para que a CPU possa acessar os registradores sem precisar interagir com a memória em um cartão de memória RAM.

Portanto, o resultado matemático de 0xA5 vezes 0xCB é 0x82D7.

Agora, por que os bits foram divididos nos registros A e D em vez dos registros A e B ou nos registros C e D? Bem, mais uma vez, este é um cenário de amostra que estou usando, cujo conceito é bastante semelhante a uma linguagem Assembly real (Intel x86 de 16 bits, conforme usado pelos Intel 8080 e 8088 e muitos CPUs mais recentes). Pode haver algumas regras comuns, como o registro "C" normalmente sendo usado como um índice para operações de contagem (típico para loops) e o registro "B" sendo usado para acompanhar as compensações que ajudam a especificar locais de memória. Portanto, "A" e "D" podem ser mais comuns para algumas das funções aritméticas comuns.

Cada instrução da CPU deve ter alguma documentação, usada por pessoas que programam no Assembly. Essa documentação deve especificar quais registros serão usados ​​por cada instrução. (Portanto, a escolha sobre quais registradores usar é geralmente especificada pelos projetistas da CPU, não pelos programadores da linguagem Assembly. Embora possa haver alguma flexibilidade.)

Agora, voltando ao "40" no exemplo acima: isso é uma série de bits, geralmente chamada de "registro de sinalizadores". Cada bit no registro de sinalizadores tem um nome. Por exemplo, há um bit de "estouro" que a CPU pode definir se o resultado for maior que o espaço que pode armazenar um byte dos resultados. (O bit "overflow" pode frequentemente ser chamado pelo nome abreviado de "OF". Isso é o capital, não o zero.) O software pode verificar o valor desse sinalizador e observar o "problema". O trabalho com esse bit geralmente é tratado de forma invisível por linguagens de nível superior; portanto, os programadores iniciantes geralmente não aprendem sobre como interagir com os sinalizadores da CPU. No entanto, os programadores de montagem geralmente podem acessar alguns desses sinalizadores de maneira muito semelhante a outras variáveis.

Por exemplo, você pode ter várias instruções ADD. Uma instrução ADD pode armazenar 16 bits de resultados no registro A e no registro D, enquanto outra instrução pode apenas armazenar os 8 bits baixos no registro A, ignorar o registro D e especificar o bit de estouro. Então, mais tarde (depois de armazenar os resultados do registro A na RAM principal), você poderá usar outra instrução ADD que armazene apenas os 8 bits altos em um registro (possivelmente o registro A.) depende apenas de qual instrução de multiplicação você usa.

(Geralmente, também existe um sinalizador "underflow", caso você subtraia muito para caber no resultado desejado.)

Apenas para mostrar como as coisas ficaram complicadas:
o Intel 4004 era uma CPU de 4 bits
O Intel 8008 era uma CPU de 8 bits. Tinha registros de 8 bits denominados A, B, C e D.
O Intel 8086 era uma CPU de 16 bits. Tinha registros de 16 bits denominados AX, BX, CX e DX.
O Intel 80386 era uma CPU de 32 bits. Ele tinha registros de 32 bits chamados EAX, EBX, ECX e EDX.
As CPUs Intel x64 possuem registros de 64 bits denominados RAX, RBX, RCX e RDX. Os chips x64 podem executar código de 16 bits (em alguns modos de operação) e podem interpretar instruções de 16 bits. Ao fazer isso, os bits que compõem o registro AX são metade dos bits que compõem o registro EAX, que são metade dos bits que compõem o registro RAX. Portanto, sempre que você altera o valor do AX, também altera o EAX e o RAX, porque esses bits usados ​​pelo AX fazem parte dos bits usados ​​pelo RAX. (Se você alterar o EAX por um valor múltiplo de 65.536, os 16 bits baixos não serão alterados para que o AX não seja alterado. Se você alterar o EAX por um valor que não seja múltiplo de 65.536, isso também afetaria o AX .)

Existem mais sinalizadores e registros do que os que eu mencionei. Simplesmente escolhi alguns usados ​​para fornecer um exemplo conceitual simples.

Agora, se você estiver em uma CPU de 8 bits, ao gravar na memória, poderá encontrar algumas restrições sobre poder se referir a um endereço de 8 bits, não a um endereço de 4 bits ou 16 bits. Os detalhes variam de acordo com a CPU, mas se você tiver essas restrições, a CPU poderá lidar com palavras de 8 bits, e é por isso que a CPU é mais conhecida como "CPU de 8 bits".


Sinto que partes da minha resposta reiteram algumas das outras respostas a esta pergunta. No entanto, isso não foi percebido quando escrevi o conteúdo pela primeira vez desde que o escrevi para outra pergunta. Além disso, embora eu aprecie a resposta do Animism, incluindo algum código na linguagem C, senti que meu conteúdo forneceu alguns detalhes sobre como o Assembly funciona, mais próximo das ações / design reais da CPU. Portanto, minha resposta não está tentando ser a resposta superior que é "melhor que" todas as outras, mas apenas suplementar; adicionando outra perspectiva com algumas dicas adicionais
TOOGAM
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.