Como a ULA em um microprocessador diferenciaria um número assinado -7, indicado por 1111, e um número não assinado 15, também indicado por 1111?
Como a ULA em um microprocessador diferenciaria um número assinado -7, indicado por 1111, e um número não assinado 15, também indicado por 1111?
Respostas:
A resposta curta e simples é: não. Nenhum ISA de CPU mainstream moderno funciona da maneira que você pensa.
Para a CPU, é apenas um pouco padrão. Cabe a você, programador, acompanhar o que esse padrão de bit significa.
Em geral, os ISAs não fazem distinção entre diferentes tipos de dados, quando se trata de armazenamento. (Ignorando registros de finalidade especial, como registros flutuantes em uma FPU.) É apenas um padrão sem sentido de bits para a CPU. No entanto, ISA fazer têm diferentes tipos de instruções que pode interpretar o padrão de bits de diferentes maneiras. Por exemplo, as instruções aritméticas tais como MUL
, DIV
, ADD
, SUB
interpretar o padrão de bits como algum tipo de número, ao passo que as instruções lógicas, tais como AND
, OR
, XOR
interpretar como uma matriz de valores booleanos. Portanto, cabe ao programador (ou ao autor do intérprete ou compilador se você usar uma linguagem de nível superior) escolher as instruções corretas.
Pode muito bem haver instruções separadas para números assinados versus números não assinados, por exemplo. Alguns ISAs também têm instruções para aritmética com decimais codificados em binário.
No entanto, observe que escrevi "ISA mainstream moderno" acima. De fato, existem ISAs não tradicionais ou históricas que funcionam de maneira diferente. Por exemplo, o CISC ISA original de 48 bits do IBM AS / 400, bem como o RISC ISA de 64 bits baseado em POWER do sistema agora chamado IBM i, distinguem entre ponteiros e outros valores. Os ponteiros são sempre marcados e incluem informações de tipo e gerenciamento de direitos. A CPU sabe se um valor é um ponteiro ou não, e apenas o kernel do i / OS privilegiado tem permissão para manipular ponteiros livremente. Os aplicativos do usuário podem manipular apenas os ponteiros que possuem para apontar para a memória que possuem, usando um pequeno número de instruções seguras.
Havia também alguns projetos históricos do ISA que incluíam pelo menos alguma forma limitada de reconhecimento de tipo.
char
, que é um tipo não assinado de 16 bits. Obviamente, ainda não há instruções aritméticas sem sinal no bytecode Java, pois qualquer char
valor é automaticamente promovido para int
(aritmético).
Versão curta: não sabe. Não há como dizer.
Se 1111
representa -7, você tem uma representação de magnitude do sinal , onde o primeiro bit é o sinal e o restante dos bits é a magnitude. Nesse caso, a aritmética é um pouco complicada, pois uma adição não assinada e uma adição assinada usam lógica diferente. Então você provavelmente teria um SADD
e um UADD
código de operação, e se você escolher o errado, obtém resultados sem sentido.
Mais frequentemente, porém, 1111
representa -1, na chamada representação de complemento de dois . Nesse caso, a ALU simplesmente não se importa se os números estão assinados ou não! Por exemplo, vamos assumir a operação de 1110 + 0001
. Na aritmética assinada, isso significa "-2 + 1", e o resultado deve ser -1 ( 1111
). Na aritmética não assinada, isso significa "14 + 1", e o resultado deve ser 15 ( 1111
). Portanto, a ALU não sabe se você deseja um resultado assinado ou não assinado e não se importa. Ele apenas adiciona como se não tivesse assinado e, se você quiser tratar isso como um número inteiro assinado posteriormente, isso é com você.
Edição: Como Ruslan e Daniel Schepler apontam com razão nos comentários, alguns operandos ainda precisam de versões assinadas e não assinadas separadas, mesmo em uma máquina com complemento de dois. Adição, subtração, multiplicação, igualdade e outras coisas funcionam bem sem saber se os números estão assinados ou não. Mas a divisão e qualquer comparação maior que / menor que o necessário precisam ter versões separadas.
EDIT EDIT: Existem algumas outras representações também, como o complemento de alguém, mas essas basicamente nunca são mais usadas, portanto você não precisa se preocupar com elas.
<
<=
>=
>
são diferentes para operandos assinados versus não assinados ==
e !=
são independentes de assinatura.
Uma das grandes vantagens da matemática de complemento de dois, que todas as arquiteturas modernas usam, é que as instruções de adição e subtração são exatamente as mesmas para operandos assinados e não assinados.
Muitas CPUs nem sequer têm instruções de multiplicar, dividir ou módulo. Se o fizerem, eles devem ter formulários de instrução assinados e não assinados separados, e o compilador (ou o programador em linguagem assembly) escolhe o apropriado.
As CPUs também geralmente têm instruções diferentes para comparações assinadas ou não assinadas. Por exemplo, x86 pode seguir a CMP
com JL
(Ir se for menor que) se a comparação for assinada ou JB
(Ir se abaixo) se a comparação não tiver sinal. Novamente, o compilador ou o programador escolheria a instrução correta para o tipo de dados.
Algumas outras instruções geralmente vêm em variantes assinadas e não assinadas, como deslocamento à direita ou carregar um valor em um registro mais amplo, com ou sem extensão de sinal.
smulh
e umulh
que retornam apenas os bits superiores da multiplicação e instruções assinadas e não assinadas que retorne o resultado em um registro com o dobro da largura dos operandos de origem.
Não faz. O processador depende do conjunto de instruções para informar que tipo de dados ele está visualizando e para onde enviá-lo. Não há nada sobre 1s e 0s no próprio operando que possa inerentemente sinalizar para a ALU se os dados são char, float, int, assinado int etc. etc. Se esse 1111 estiver indo para um circuito elétrico que espera um complemento de 2s, será para ser interpretado como um complemento 2s.
char
no nível de hardware. Talvez uma vez, nos dias de tele-impressoras mecânicas. Mas hoje, a char
é apenas um número no que diz respeito ao hardware. A razão pela qual números diferentes correspondem a diferentes formas de letras na tela é que esses números são usados para selecionar bitmaps diferentes ou diferentes rotinas de desenho de uma tabela grande (ou seja, de uma "fonte").
Eu gostaria de acrescentar as respostas já feitas:
Na maioria das outras respostas, note-se que na aritmética de dois complementos o resultado é o mesmo para números assinados e não assinados:
-2 + 1 = -1 1110 + 0001 = 1111
14 + 1 = 15 1110 + 0001 = 1111
No entanto , existem exceções:
Division:
-2 / 2 = -1 1110 / 0010 = 1111
14 / 2 = 7 1110 / 0010 = 0111
Comparison:
-2 < 2 = TRUE 1110 < 0010 = TRUE
14 < 2 = FALSE 1110 < 0010 = FALSE
"Typical" (*) multiplication:
-2 * 2 = -4 1110 * 0010 = 11111100
14 * 2 = 28 1110 * 0010 = 00011100
(*) Em muitas CPUs, o resultado de uma multiplicação de dois números de n bits tem (2 * n) bits de largura.
Para tais operações, as CPUs possuem instruções diferentes para aritmética assinada e não assinada.
Isso significa que o programador (ou o compilador) deve usar outras instruções para aritmética assinada e não assinada.
A CPU x86, por exemplo, possui uma instrução nomeada div
para executar uma divisão não assinada e uma instrução nomeada idiv
para executar uma divisão assinada.
Também existem instruções "condicionais" diferentes (saltos condicionais, configuração de bit com condição), bem como instruções de multiplicação para aritmética assinada e não assinada.