Por que "$ ((~ 33))" produz -34?


12
$ echo $(( 255 ))
255
$ echo $(( 33 ))
33
$ echo $(( ~33 ))
-34
$ echo $(( ~255 ))
-256
$ 

e meu kernel é:

$ uname -a
Linux HOSTNAME 3.2.0-40-generic-pae #64-Ubuntu SMP Mon Mar 25 21:44:41 UTC 2013 i686 i686 i386 GNU/Linux

PERGUNTA: ~ é para negar o número AFAIK. Mas por que ~33produzir -34e por que ~255produzir -256?


2
Bit a bit negação, não deve ser confundida com a negação aritmética ( -x )
chepner

Respostas:


21

A página de manual do bash diz:

   ! ~    logical and bitwise negation

Os números assinados são geralmente armazenados na representação do complemento do Two :

...
-4 = 1100
-3 = 1101
-2 = 1110
-1 = 1111
 0 = 0000
 1 = 0001
 2 = 0010
 3 = 0011
...

Isso significa que, se você pegar um número como 2, ele será interpretado em bits como 0010. Após a negação em bits, isso se tornará 1101, que é a representação de -3.


10

Este é o resultado da aritmética do complemento de dois.

~é uma negação bit a bit que inverte todos os bits que estão sendo operados. A aritmética do complemento de dois funciona invertendo todos os bits e adicionando 1. Como você só inverteu os bits, mas não adicionou um, obtém o mesmo número, invertido, menos um.

A Wikipedia tem um bom artigo sobre o complemento de dois aqui .

Como um exemplo:

  • 3 em binário é 0011
  • -3 in (complemento de dois) binário é 1101
  • A inversão 0011fornece 1100-4, pois você não adicionou 1.

3

O operador ~ é o operador NOT bit a bit. Usá-lo não é o mesmo que negar um número.

Na wikipedia , uma operação NOT bit a bit é igual a obter o complemento de dois do valor menos um:

NÃO x = −x - 1

Negar um número binário é equivalente a obter seu valor de dois complementos.

Usando o operador ~ NOT = use seu valor de um complemento.

Em termos mais simples, ~ apenas inverte todos os bits da representação binária .

Para seus exemplos:

33 (decimal) = 0x00100001 (binário de 8 bits)

~ 33 = ~ 0x00100001 = 0x11011110 = -34 (decimal)

Ou na aritmética decimal, usando a fórmula ~ x = -x - 1:

~ 33 = -33 - 1 = -34

e

~ 255 = -255 - 1 = -256


1

O problema é que ~ é um operador pouco inteligente. Portanto, você está negando mais bits do que talvez pretenda. Você pode ver isso melhor convertendo os resultados em hexadecimal, por exemplo:

result_in_hex=$(printf "%x" $(( ~33 ))); echo $result_in_hex
ffffffffffffffde

versus o que você tinha:

result_in_dec=$(printf "%d" $(( ~33 ))); echo $result_in_dec
-34

Suponho que você pretenda negar 0x33. Se for esse o caso, isso funcionaria:

result_in_hex=$(printf "%2x" $(( ( ~ 0x33 ) & 0xFF))); echo $result_in_hex
cc

Você também deve usar & qual é o operador e bit-wise para evitar todo o ff no início.


1

O ~operador (aritmético) vira todos os bits , é chamado de operador de negação bit a bit:

! ~    logical and bitwise negation

Portanto, em locais onde o contexto é aritmético, ele altera um número com todos os bits como zeros para todos os bits como um. A $(( ~0 ))converte todos os bits da representação numérica (normalmente hoje em dia, 64 bits) em todos.

$ printf '%x\n' "$(( ~0 ))"
ffffffffffffffff

Um número com todos os números é interpretado como o número negativo (primeiro bit 1) 1, ou simplesmente -1.

$ printf '%x\n' "-1"
ffffffffffffffff

$ echo "$(( ~0 ))"
-1

O mesmo acontece com todos os outros números, por exemplo: $(( ~1 ))vira todos os bits:

$ printf '%x\n' "$(( ~1 ))"
fffffffffffffffe

Ou, em binário: 1111111111111111111111111111111111111111111111111111111111111110

O qual, interpretado como um número na representação de dois, é:

$ echo "$(( ~1 ))"
-2

Em geral, a equação matemática humana $(( ~n ))é igual a$(( -n-1 ))

$ n=0    ; echo "$(( ~n )) $(( -n-1 ))"
-1 -1

$ n=1    ; echo "$(( ~n )) $(( -n-1 ))"
-2 -2

$ n=255  ; echo "$(( ~n )) $(( -n-1 ))"
-256 -256

E (sua pergunta):

$ n=33   ; echo "$(( ~n )) $(( -n-1 ))"
-34 -34

0

Primeiro você precisa entender que 33 é um número de 32 bits ou de 64 bits.

Por conveniência, pego um número de oito bits (= 1 byte)

decimal 33 está em oito bits: 00100001, invertendo os bits resulta em 11011110.

Como o bit de ordem superior é 1, é um número negativo.

Imprimindo um número negativo, o sistema imprime um sinal de menos e depois faz um complemento de dois no número negativo.

O complemento de dois é: virar os bits e adicionar 1.

11011110 ==> 00100001 ==> adicionar 1 ==> 00100010 resulta em 34 decimal atrás do sinal de menos.

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.