Pergunta

Em C, são a mudança operadores (<<, >>) aritmética ou lógica?

Foi útil?

Solução

De acordo com a K&R 2ª edição os resultados são dependente de implementação para a direita mudanças de valores assinados.

Taxas diz-se que C/C++ 'normalmente' implementa uma aritmética a mudança de valores assinados.

Basicamente o que você precisa para testar o seu compilador ou não contar com ele.Meu VS2008 ajuda para o atual MS C++ compilador diz que o compilador faz uma aritmética shift.

Outras dicas

Quando deslocar para a esquerda, não há nenhuma diferença entre a aritmética e lógica de mudança.Quando deslocar para a direita, o tipo de mudança depende do tipo do valor a ser deslocado.

(Como plano de fundo para os leitores não familiarizados com a diferença, uma "lógica" shift direita por 1 bit muda todos os bits para a direita e preenche o bit mais à esquerda com um 0.Uma "aritmética" shift deixa o valor original o bit mais à esquerda.A diferença torna-se importante quando se lida com números negativos.)

Ao deslocar um valor sem sinal, > > operador em C é uma lógica de mudança.Ao deslocar um valor assinado, > > operador é uma aritmética shift.

Por exemplo, supondo que uma máquina de 32 bits:

signed int x1 = 5;
assert((x1 >> 1) == 2);
signed int x2 = -5;
assert((x2 >> 1) == -3);
unsigned int x3 = (unsigned int)-5;
assert((x3 >> 1) == 0x7FFFFFFD);

TL;DR

Considere i e n para ser a esquerda e para a direita operandos, respectivamente, de uma mudança de operador;o tipo de i, após inteiro promoção, ser T.Supondo n para estar em [0, sizeof(i) * CHAR_BIT) — não definido de outra forma — temos, nestes casos:

| Direction  |   Type   | Value (i) | Result                   |
| ---------- | -------- | --------- | ------------------------ |
| Right (>>) | unsigned |    ≥ 0    | −∞ ← (i ÷ 2ⁿ)            |
| Right      | signed   |    ≥ 0    | −∞ ← (i ÷ 2ⁿ)            |
| Right      | signed   |    < 0    | Implementation-defined†  |
| Left  (<<) | unsigned |    ≥ 0    | (i * 2ⁿ) % (T_MAX + 1)   |
| Left       | signed   |    ≥ 0    | (i * 2ⁿ) ‡               |
| Left       | signed   |    < 0    | Undefined                |

† a maioria dos compiladores implementar isso como aritmética shift
‡ indefinido, se o valor excede o de resultado tipo T;promovido tipo de eu


Mudança

O primeiro é a diferença entre lógica e aritmética desloca de um ponto de vista matemático, sem se preocupar com o tamanho de tipo de dados.Lógico turnos sempre preenche descartados bits com zeros, enquanto aritmética shift preenche com zeros apenas para deslocar para a esquerda, mas para a direita shift copia o MSB preservando assim o sinal do operando (supondo um complemento de dois codificação para valores negativos).

Em outras palavras, o deslocamento lógico olha deslocada operando apenas como uma sequência de bits e movê-los, sem se preocupar com o sinal do valor resultante.Aritmética shift vê isso como uma (assinado) número e preserva o sinal de que mudanças são feitas.

Uma esquerda shift aritmético de um número X por n é equivalente a multiplicar X por 2n e é, portanto, equivalente à lógica de deslocamento à esquerda;uma lógica de mudança também daria o mesmo resultado, desde MSB enfim chega ao fim e não há nada para preservar.

O direito aritmética mudança de um número X por n é equivalente à divisão de X por 2n SOMENTE se X é não-negativo!Divisão de número inteiro não é nada, mas matemática divisão e rodada para 0 (trunc).

Para números negativos, representados por duas complemento de codificação, deslocando para a direita por n bits tem o efeito de, matematicamente, ao dividi-lo por 2n e arredondamento para −∞ (chão);assim, o direito de deslocamento é diferente para não-negativos e negativos.

para X ≥ 0, X >> n = X / 2n = trunc(X ÷ 2n)

para X < 0, X >> n = floor(X ÷ 2n)

onde ÷ é matemática divisão, / é a divisão de número inteiro.Vejamos um exemplo:

37)10 = 100101)2

37 ÷ 2 = 18.5

37 / 2 = 18 (arredondamento de 18,5 para 0) = 10010)2 [resultado da aritmética para a direita shift]

-37)10 = 11011011)2 (considerando-se um complemento de dois, de 8 bits de representação)

-37 ÷ 2 = -18.5

-37 / 2 = -18 (arredondamento de 18,5 para 0) = 11101110)2 [NÃO o resultado da aritmética para a direita shift]

-37 >> 1 = -19 (arredondamento de 18,5 para −∞) = 11101101)2 [resultado da aritmética para a direita shift]

Como Guy Steele apontou, esta discrepância levou a erros em mais de um compilador.Aqui não negativo (matemática) pode ser mapeado para não assinados e assinado valores não-negativos (C);ambos são tratados da mesma e clique com o botão direito deslocamento deles é feito pela divisão de número inteiro.

Tão lógica e aritmética são equivalentes na esquerda-deslocamento e para valores não-negativos no direito de deslocamento;é no direito de mudança de valores negativos que eles diferem.

Operando e Tipos de Resultado

Padrão C99 §6.5.7:

Cada um dos operandos devem ter tipos inteiros.

O inteiro promoções são realizadas em cada um dos operandos.O tipo do resultado é que o operando esquerdo promovido.Se o valor do operando direito é negativo ou maior que ou igual à largura do operando esquerdo promovido, o comportamento é indefinido.

short E1 = 1, E2 = 3;
int R = E1 << E2;

No trecho acima, ambos os operandos tornar int (devido ao número inteiro de promoção);se E2 foi negativo ou E2 ≥ sizeof(int) * CHAR_BIT em seguida, a operação é indefinido.Isto é porque a deslocar mais do que o bits é certamente vai transbordar.Tinha R sido declarada como short, o int resultado da operação de deslocamento iria ser implicitamente convertido para short;uma conversão de restrição, que pode levar à implementação definido pelo comportamento se o valor não é representável no tipo de destino.

Shift Esquerdo

O resultado de E1 << E2 E1 é de esquerda deslocada E2 posições de bit;desocupado bits são preenchidos com zeros.Se E1 tem um tipo não assinado, o valor do resultado é E1×2E2, redução do módulo um a mais do que o valor máximo representáveis no tipo de resultado.Se E1 tem uma assinado o tipo e o não-valor negativo, e E1×2E2 é representáveis no tipo de resultado e, em seguida, que é o valor resultante;caso contrário, o comportamento é indefinido.

Como a esquerda desloca são as mesmas para ambos, o desocupado bits são simplesmente preenchidos com zeros.Em seguida, ele afirma que para tanto não assinados e assinado tipos é uma aritmética shift.Eu estou interpretando-a como aritmética shift desde lógico turnos de não se preocupar com o valor representado por bits, ele apenas olha para ele como um fluxo de bits;mas a norma não fala em termos de bits, mas definindo-a em termos do valor obtido pelo produto de E1, com 2E2.

O truque aqui é que, para assinada tipos de valor deve ser não-negativo e o valor resultante deve ser representáveis no tipo de resultado.Caso contrário, a operação é indefinido. O tipo de resultado seria o tipo de E1 após a aplicação de promoção integral, e não o de destino (a variável que vai armazenar o resultado) tipo.O valor resultante é implicitamente convertido para o tipo de destino;se não representáveis em que tipo e, em seguida, a conversão é definido para implementação (C99 §6.3.1.3/3).

Se E1 é assinado um tipo com um valor negativo, em seguida, o comportamento da esquerda mudança é indefinido. Esta é uma rota fácil para indefinido comportamentos que podem facilmente passar despercebidos.

Shift Direita

O resultado de E1 >> E2 E1 é clique com o botão direito deslocado E2 posições de bit.Se E1 tem um tipo não assinado ou se E1 tem assinado um tipo e um valor negativo, o valor do resultado é a parte integral do quociente de E1/2E2.Se E1 tem assinado um tipo e um valor negativo, o valor resultante é definido para implementação.

Shift direita por não assinados e assinado valores não-negativos são bastante direta;a vaga de bits são preenchidos com zeros. Assinado valores negativos o resultado do direito de deslocamento é definido para implementação. O que disse, a maioria das implementações, como o GCC e O Visual C++ implementar deslocamentos à direita como aritmética mudança preservando o bit de sinal.

Conclusão

Ao contrário de Java, que tem um operador especial >>> para o desvio lógico, para além do habitual >> e <<, C e C++ têm apenas aritmética mudando com algumas áreas deixado indefinido e definido para implementação.A razão de eu considerar eles como a aritmética é devido para a formulação padrão de operação matematicamente, ao invés de tratar o deslocado operando como um fluxo de bits;esta é talvez a razão pela qual ele deixa as áreas da onu/definido para implementação em vez de apenas a definição de todos os casos, a lógica de turnos.

Em termos do tipo de mudança que você conseguir, o importante é o tipo do valor que você está se deslocando.Uma clássica fonte de erros é quando você mudar de uma literal para, digamos, a máscara de bits.Por exemplo, se você queria largar mais à esquerda os bits de um número inteiro não assinado, em seguida, você pode tentar este como a sua máscara:

~0 >> 1

Infelizmente, isso vai causar problemas porque a máscara vai ter todos os seus bits definido como o valor a ser deslocado (~0) é assinado, assim, uma aritmética mudança é realizada.Em vez disso, você deseja forçar uma mudança lógica por declarar explicitamente o valor como unsigned, i.é.fazendo algo parecido com isto:

~0U >> 1;

Aqui estão as funções para garantir a lógica do direito e de deslocamento aritmético à direita deslocamento de um int em C:

int logicalRightShift(int x, int n) {
    return (unsigned)x >> n;
}
int arithmeticRightShift(int x, int n) {
    if (x < 0 && n > 0)
        return x >> n | ~(~0U >> n);
    else
        return x >> n;
}

Quando você fizer - shift da esquerda por 1, multiplique por 2 - shift direita por 1 a dividir por 2

 x = 5
 x >> 1
 x = 2 ( x=5/2)

 x = 5
 x << 1
 x = 10 (x=5*2)

Bem, eu olhei - a na wikipédia, e eles têm a dizer:

C, no entanto, tem apenas um shift direita operador >>.Muitos compiladores C escolha que a direita shift para executar a função em que tipo de número inteiro está sendo deslocado;muitas vezes, inteiros assinados são mudou usando a aritmética de mudança, e inteiros são deslocadas usando a lógica shift.

Então, parece que depende do seu compilador.Também nesse artigo, note que o shift da esquerda é o mesmo para operações aritméticas e lógicas.Eu recomendaria fazer um simples teste com alguns números assinados e não assinados na fronteira de caso (conjunto de bits alta, claro) e ver que o resultado é no seu compilador.Eu também recomendaria evitar dependendo sendo um ou outro, pois parece C não tem padrão, pelo menos se for razoável e possível, para evitar tal dependência.

Shift esquerdo <<

Este é, de alguma forma fácil e sempre que você usar o operador shift, é sempre um bit de operação, de modo que não pode usá-lo com um double e float operação.Sempre que saímos de mudança de um a zero, é sempre adicionado ao bit menos significativo (LSB).

Mas em shift direita >> temos que seguir uma regra e essa regra é chamado de "bit de sinal copiar".Significado de "bit de sinal copiar" se o bit mais significativo (MSB) é definida, em seguida, depois de um shift direita novamente o MSB será definido se ela foi redefinida, em seguida, é repor novamente, significa que, se o valor anterior era zero, em seguida, depois de deslocar novamente, o bit é zero se o anterior foi um pouco depois que a mudança é novamente um.Esta regra não é aplicável para a esquerda shift.

O exemplo mais importante no direito de mudar se você mudar qualquer número negativo para a direita shift e, em seguida, depois de algum mudando o valor para finalmente chegar a zero e, em seguida, depois de este se mudar essa -1 qualquer número de vezes que o valor permanecerá o mesmo.Verifique por favor.

normalmente usa lógica turnos não assinado variáveis e para a esquerda desloca sobre assinado variáveis.O aritmético para a direita shift é o verdadeiramente importante porque ele vai assinar estender a variável.

vai usar isso quando aplicável, como outros compiladores são propensos a fazer.

O GCC faz

  1. de -ve - > Shift Aritmético

  2. Para +ve -> Deslocamento Lógico

De acordo com muitos compiladores:

  1. << é uma aritmética shift esquerda ou para a esquerda bit a bit shift.
  2. >> é uma aritmética direito shiftor bit a bit deslocamento à direita.
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top