Почему «$ ((~ 33))» производит -34?


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

и мое ядро:

$ 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

ВОПРОС: ~ для отрицания числа AFAIK. Но почему ~33производит -34и почему ~255производит -256?


2
Побитовое отрицание, не следует путать с арифметическим отрицанием ( -x )
chepner

Ответы:


21

Страница руководства bash гласит:

   ! ~    logical and bitwise negation

Числа со знаком обычно хранятся в дополнительном представлении Two :

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

Это означает, что если вы возьмете число, подобное 2, оно будет поразрядно интерпретировано как 0010. После побитового отрицания это становится 1101, что является представлением -3.


10

Это результат дополнения арифметики двух.

~это побитовое отрицание, которое инвертирует все биты, с которыми работают. Арифметика дополнения двух работает, инвертируя все биты и добавляя 1. Поскольку вы только перевернули биты, но не добавили один, вы получите то же самое число, инвертированное, минус один.

В Википедии есть хорошая статья о двух дополнениях здесь .

В качестве примера:

  • 3 в двоичном виде 0011
  • -3 в (два дополнения) бинарный 1101
  • Инвертирование 0011дает вам 1100, что -4, так как вы не добавили 1.

3

Оператор ~ является побитовым оператором NOT. Использование это не то же самое, что отрицание числа.

Из википедии побитовая операция NOT равна получению дополнения до значения минус один:

НЕ x = −x - 1

Отрицание двоичного числа эквивалентно принятию его значения из двух дополнений.

Использование оператора ~ NOT = принимает значение, состоящее из одного дополнения.

Проще говоря, ~ просто переворачивает все биты двоичного представления .

Для ваших примеров:

33 (десятичное число) = 0x00100001 (8-разрядный двоичный код)

~ 33 = ~ 0x00100001 = 0x11011110 = -34 (десятичное число)

Или в десятичной арифметике, используя формулу ~ x = -x - 1:

~ 33 = -33 - 1 = -34

и

~ 255 = -255 - 1 = -256


1

Проблема в том, что ~ является побитовым оператором. Следовательно, вы отрицаете больше битов, чем, возможно, намереваетесь. Вы можете увидеть это лучше, преобразовав результаты в гекс, например:

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

по сравнению с тем, что у вас было:

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

Я предполагаю, что вы хотите отрицать 0x33. Если это так, то это будет работать:

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

Вы также должны использовать &, который является побитовым и оператором, чтобы избежать всех ff в начале.


1

Оператор ~(арифметический) переворачивает все биты , он называется оператором побитового отрицания:

! ~    logical and bitwise negation

Таким образом, в местах, где контекст является арифметическим, он меняет число со всеми битами в виде нулей на все биты в единицах. A $(( ~0 ))преобразует все биты числового представления (обычно в настоящее время 64 бита) во все.

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

Число со всеми интерпретируется как отрицательное число (первый бит 1) 1или просто -1.

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

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

То же самое происходит со всеми другими числами, например: $(( ~1 ))переворачивает все биты:

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

Или в двоичном виде: 1111111111111111111111111111111111111111111111111111111111111110

Что интерпретируется как число в представлении два:

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

В общем, математическое уравнение человека таково, что $(( ~n ))равно$(( -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

И (ваш вопрос):

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

0

Сначала вы должны понять, что 33 - это 32-битное или 64-битное число.

Для удобства я беру восьмибитное число (= 1 байт)

десятичное число 33 состоит из восьми битов: 00100001, переворачивание битов приводит к 11011110.

Поскольку старший бит равен 1, это отрицательное число.

Распечатывая отрицательное число, система печатает знак минус и затем дополняет двоичное число отрицательным числом.

Второе дополнение: переворачивание битов и добавление 1.

11011110 ==> 00100001 ==> добавление 1 ==> 00100010 приводит к десятичному знаку 34 после знака минус.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.