Números assinados e não assinados

17

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?

noorav
fonte
3
Consulte a resposta da pergunta relacionada: cs.stackexchange.com/a/30047/28999 . A propósito, o sinal -7 assinado não é representado como 1111. Isso é -1. Em seguida, por exemplo, 1111 - 0001 = 1110, tanto no caso de sinal e sem sinal (vs -2 14)
Albert Hendriks
2
@AlbertHendriks Para ser justo, alguns computadores mais antigos usam uma "representação de magnitude de sinal" (um bit de sinal e bits de magnitude ), e ainda usamos esse estilo para, por exemplo, flutuadores IEEE. Eles são apenas deselegantes e difíceis de trabalhar em comparação com o complemento de dois. n-1
Draconis
1
A principal diferença está na maneira como os operadores maior ou menor do que o que se comporta e se o deslocamento à direita é preenchido com o bit mais alto. Quando você realmente se multiplica e divide, o resultado é o mesmo.
Rob
2
@ Rob Isso não está totalmente correto. Adição, subtração e multiplicação são iguais entre o complemento não assinado e o duplo - assumindo que suas entradas e saídas são do mesmo tamanho. Divisão não é a mesma 6/2 é 3, mas -2/2 é -1. E muitas CPUs têm instruções de multiplicação em que as duas entradas são de tamanho idêntico, mas a saída é o dobro do tamanho; nesse caso, o sinal não assinado e o complemento duplo também não são os mesmos.
kasperd

Respostas:

14

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, SUBinterpretar o padrão de bits como algum tipo de número, ao passo que as instruções lógicas, tais como AND, OR, XORinterpretar 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.

Jörg W Mittag
fonte
Observe que o bytecode Java também conta como um ISA. E praticamente se preocupa com os tipos de dados ...
John Dvorak
O bytecode Java conta meio que como ISA, no sentido de que foi implementado em silício. No entanto, a verificação de tipo básico desse tipo é realizada pelo carregador de classes, portanto, os tipos podem ser ignorados principalmente em tempo de execução. E, é claro, o bytecode Java não possui tipos não assinados.
Pseudônimo
@Pseudonym: Bem, tecnicamente, ele não tem 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 charvalor é automaticamente promovido para int(aritmético).
Ilmari Karonen 12/03/19
42

Versão curta: não sabe. Não há como dizer.

Se 1111representa -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 SADDe um UADDcódigo de operação, e se você escolher o errado, obtém resultados sem sentido.

Mais frequentemente, porém, 1111representa -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.

Draconis
fonte
Ah, entendi. Obrigado por isso :)
noorav 11/03/19
10
Na representação do complemento de dois, três operações aritméticas são independentes da assinatura: adição, subtração e multiplicação (com produto do mesmo comprimento que os operandos). Somente a divisão deve ser tratada de maneira diferente para operandos assinados.
Ruslan
4
Também há comparação: < <= >= >são diferentes para operandos assinados versus não assinados ==e !=são independentes de assinatura.
Daniel Schepler
A multiplicação geralmente possui variedades assinadas e não assinadas: 0xFFFFFFFF * 0xFFFFFFFF é 0xFFFFFFFE00000001 se não tiver assinado e 0x0000000000000001 se assinado. Processadores como a Intel retornam o resultado em 2 registros, e o registro superior é diferente para assinado e não assinado. O registro inferior é 1 em ambas as situações.
Rudy Velthuis 12/03/19
9

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 CMPcom 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.

Davislor
fonte
1
Mesmo a multiplicação é a mesma para números inteiros não assinados e assinados (complemento de dois), desde que você não precise que o resultado tenha mais bits do que as entradas . Se você estiver fazendo algo como multiplicação 8 × 8 → 16 bits (ou 16 × 16 → 32 bits, etc.), no entanto, precisará assinar estender as entradas (ou os resultados intermediários) .
Ilmari Karonen 12/03/19
@IlmariKaronen Isso é verdade; ARM A32 / A64 são conjuntos de instruções que possuem muitas formas da instrução multiplicar, incluindo adição múltipla independente de sinal que grava apenas os bits de ordem inferior, mas também smulhe umulhque 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.
Davislor
6

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.

Jay Speidell
fonte
Não existe charno 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").
Solomon Slow
3

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 divpara executar uma divisão não assinada e uma instrução nomeada idivpara 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.

Martin Rosenau
fonte