ทำไม“ $ ((~ 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

หน้าคนของทุบตีพูดว่า:

   ! ~    logical and bitwise negation

หมายเลขที่ลงชื่อมักจะถูกเก็บไว้ในการเป็นตัวแทนของ Two :

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

ซึ่งหมายความว่าหากคุณใช้ตัวเลขเช่น 2 มันจะเป็นการตีความแบบ bitwise เป็น 0010 หลังจากการลบล้าง bitwise กลายเป็น 1101 ซึ่งเป็นการแทน -3


10

นี่คือผลลัพธ์ของเลขคณิตประกอบสอง

~เป็นการปฏิเสธในระดับบิตที่แปลงกลับบิตทั้งหมดที่ถูกดำเนินการ การคำนวณทางคณิตศาสตร์ที่สมบูรณ์ของสองทำงานโดยการแปลงบิตทั้งหมดและการเพิ่ม 1 เนื่องจากคุณมีการพลิกบิตเพียงอย่างเดียว แต่ไม่ได้เพิ่มเข้าไปคุณจะได้รับจำนวนเดียวกันกลับด้านลบหนึ่ง

วิกิพีเดียมีบทความที่ดีในการเติมเต็มสองที่นี่

ตัวอย่างเช่น:

  • 3 ในไบนารีคือ 0011
  • -3 ใน (ส่วนประกอบสองส่วน) คือ 1101
  • Inverting 0011ให้คุณ1100ซึ่งก็คือ -4 เนื่องจากคุณยังไม่ได้เพิ่ม 1

3

ตัวดำเนินการ ~ เป็นตัวดำเนินการระดับบิตไม่ การใช้มันไม่เหมือนกับการลบตัวเลข

จากวิกิพีเดียการดำเนินการระดับบิตไม่เท่ากับการเติมเต็มสองค่าลบ:

NOT x = −x - 1

การลบเลขฐานสองนั้นเทียบเท่ากับการรับค่าสองส่วนประกอบ

การใช้ตัวดำเนินการ ~ NOT = รับค่าเสริมเดียว

ในแง่ที่เรียบง่าย~ เพียงแค่พลิกบิตทั้งหมดของแทน

สำหรับตัวอย่างของคุณ:

33 (ฐานสิบ) = 0x00100001 (ไบนารี 8 บิต)

~ 33 = ~ 0x00100001 = 0x11011110 = -34 (ทศนิยม)

หรือในทศนิยมเลขคณิตโดยใช้สูตร ~ x = -x - 1:

~ 33 = -33 - 1 = -34

และ

~ 255 = -255 - 1 = -256


1

ปัญหาคือว่า ~ เป็นตัวดำเนินการที่ชาญฉลาด ดังนั้นคุณจะปฏิเสธบิตมากกว่าที่คุณตั้งใจ คุณสามารถดูสิ่งนี้ได้ดีขึ้นโดยแปลงผลลัพธ์ให้เป็น hex เช่น:

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 หลังเครื่องหมายลบ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.