¿Por qué "$ ((~ 33))" produce -34?

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

y mi núcleo es:

$ 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

PREGUNTA: ~ es para negar el número AFAIK. Pero, ¿por qué ~33produce -34y por qué ~255produce -256?

Gasko Peter
fuente
2
Bit a bit negación, que no debe confundirse con la negación aritmética ( -x )
chepner

Respuestas:

21

La página del manual de bash dice:

   ! ~    logical and bitwise negation

Los números firmados generalmente se almacenan en la representación complementaria de Two :

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

Esto significa que si toma un número como 2, se interpreta a nivel de bit como 0010. Después de la negación a nivel de bit, se convierte en 1101, que es la representación de -3.

michas
fuente
10

Este es el resultado de la aritmética del complemento a dos.

~es una negación bit a bit que invierte todos los bits sobre los que se opera. La aritmética del complemento a dos funciona invirtiendo todos los bits y sumando 1. Dado que solo ha volteado los bits, pero no ha agregado uno, obtiene el mismo número, invertido, menos uno.

Wikipedia tiene un buen artículo sobre el complemento a dos aquí .

Como ejemplo:

  • 3 en binario es 0011
  • -3 en binario (complemento de dos) es 1101
  • Invertir 0011te da 1100, que es -4, ya que no has agregado 1.
Chris Down
fuente
3

El operador ~ es el operador NO bit a bit. Usarlo no es lo mismo que negar un número.

De wikipedia , una operación NO a nivel de bit es igual a tomar el complemento de dos del valor menos uno:

NO x = −x - 1

Negar un número binario es equivalente a tomar su valor de dos complementos.

Usando el operador ~ NOT = tome su valor de un complemento.

En términos más simples, ~ simplemente voltea todos los bits de la representación binaria .

Para tus ejemplos:

33 (decimal) = 0x00100001 (binario de 8 bits)

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

O en aritmética decimal, usando la fórmula ~ x = -x - 1:

~ 33 = -33 - 1 = -34

y

~ 255 = -255-1 = 256

Damien
fuente
1

El problema es que ~ es un operador inteligente. Por lo tanto, está negando más bits de los que quizás pretende. Puede ver esto mejor al convertir los resultados a hexadecimal, por ejemplo:

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

versus lo que tenías:

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

Supongo que quieres negar 0x33. Si ese es el caso, entonces esto funcionaría:

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

También debe usar &, que es el bit-wise y el operador para evitar todo el ff al comienzo.

Neil McGill
fuente
1

El ~operador (aritmético) voltea todos los bits , se llama operador de negación bit a bit:

! ~    logical and bitwise negation

Entonces, en lugares donde el contexto es aritmético, cambia un número con todos los bits como ceros a todos los bits como unos. A $(( ~0 ))convierte todos los bits de la representación numérica (generalmente 64 bits hoy en día) a todos.

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

Un número con todos se interpreta como el número negativo (primer bit 1) 1, o simplemente -1.

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

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

Lo mismo ocurre con todos los demás números, por ejemplo: $(( ~1 ))voltea todos los bits:

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

O, en binario: 1111111111111111111111111111111111111111111111111111111111111110

Lo cual, interpretado como un número en la representación de dos es:

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

En general, la ecuación matemática humana $(( ~n ))es 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

Y (tu pregunta):

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

Primero debe comprender que 33 es un número de 32 bits o de 64 bits.

Por conveniencia, tomo un número de ocho bits (= 1 byte)

el decimal 33 está en ocho bits: 00100001, voltear los bits da como resultado 11011110.

Como el bit de orden superior es 1, es un número negativo.

Al imprimir un número negativo, el sistema imprime un signo menos y luego hace un complemento de dos en el número negativo.

El complemento a dos es: voltear los bits y sumar 1.

11011110 ==> 00100001 ==> sumando 1 ==> 00100010 da como resultado 34 decimales detrás del signo menos.

F. Harthoorn
fuente