为什么“ $((〜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

问题: ~用于否定数字FAIKK。但是为什么~33产生-34,为什么~255产生-256


2
按位取反,请勿与算术取反(-x)混淆
chepner 2013年

Answers:


21

bash的手册页说:

   ! ~    logical and bitwise negation

带符号的数字通常以二进制补码形式存储:

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

这意味着,如果您采用2之类的数字,则按位解释为0010。按位取反后将变为1101,即-3的表示形式。


10

这是二进制补码算法的结果。

~是按位取反,它会将所有要操作的位取反。二进制补码算术通过将所有位取反并加1来工作。由于您仅翻转了位,而未将位相加,因此得到的数字相同,取反后减一。

维基百科在这里很好地介绍了二进制补码。

举个例子:

  • 二进制3是 0011
  • -2(二进制补码)中的-3 1101
  • 倒数00111100-4,因为您尚未加1。

3

〜运算符是按位NOT运算符。使用它与否定数字不同。

Wikipedia来看,按位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

因此,在上下文是算术运算的地方,它将所有位都为零的数字更改为所有位为1的数字。A $(( ~0 ))将数字表示形式的所有位(如今通常为64位)转换为全1。

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

全为1的数字被解释为负数(第一位11,或简称为-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位数字。

为了方便起见,我使用8位数字(= 1个字节)

十进制33的8位为00100001,将这些位翻转会得到11011110。

由于高位为1,因此为负数。

打印一个负数,系统打印一个负号,然后在该负数上进行二进制补码。

二进制的补码是:翻转位并加1。

11011110 ==> 00100001 ==>加1 ==> 00100010导致减号后面的十进制数34。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.