ตัวดำเนินการ bitwise complement (~ tilde) ทำงานอย่างไร


คำตอบ:


281

โปรดจำไว้ว่าตัวเลขลบจะถูกจัดเก็บเป็นส่วนเติมเต็มของคู่ที่เป็นบวก ดังตัวอย่างต่อไปนี้เป็นตัวแทนของ -2 ในส่วนเติมเต็มของสอง: (8 บิต)

1111 1110

วิธีที่คุณจะได้รับคือการใช้เลขฐานสองแทนการเติมเต็ม (เติมบิตทั้งหมด) และเพิ่มเข้าไป สองเริ่มเป็น 0000 0010 และโดยการตัดบิตเราจะได้รับ 1111 1101 การเพิ่มหนึ่งทำให้เราได้ผลลัพธ์ข้างต้น บิตแรกคือเครื่องหมายบิตซึ่งหมายถึงค่าลบ

ลองดูวิธีที่เราได้ ~ 2 = -3:

นี่คือสองอีกครั้ง:

0000 0010

เพียงแค่พลิกบิตทั้งหมดและเราจะได้รับ:

1111 1101

ทีนี้, -3 มีลักษณะอย่างไรในสองส่วนเติมเต็ม? เริ่มต้นด้วยค่าบวก 3: 0000 0011 พลิกบิตทั้งหมดเป็น 1111 1100 และเพิ่มหนึ่งค่าเป็นค่าลบ (-3), 1111 1101

ดังนั้นถ้าคุณสลับบิตใน 2 คุณจะได้การแทนสมบูรณ์ของทั้งสองเป็น -3

ผู้ประกอบการเสริม (~) เพียงแค่พลิก BITS มันขึ้นอยู่กับเครื่องเพื่อตีความบิตเหล่านี้


43
สิ่งหนึ่งที่อื่น ๆ อาจจะพูดถึงก็คือว่าพลิกเรียกว่าสมบูรณ์ 1s ก่อนที่จะเพิ่ม 1.
คริส S

3
มันอาจช่วยให้ผู้อื่นที่ไม่ได้รับรู้ถึงการเติมเต็มของ One และ Complement ของ Two อ่านเกี่ยวกับพวกเขาที่นี่ en.wikipedia.org/wiki/Ones%27_complement en.wikipedia.org/wiki/Two%27s_complement สองครั้ง
Sai

1
นั่นไม่ใช่ตัวดำเนินการระดับบิตใช่หรือไม่
Braden ที่ดีที่สุด

3
เครื่องรู้ได้อย่างไรว่าได้รับจำนวนลบสองค่าแทนค่าบวกที่สูงขึ้น เป็นเพราะระบบประเภทของภาษานั้น ๆ ระบุว่าเป็น int ลงนามเมื่อเทียบกับไม่ได้ลงนาม?
GL2014

@ GL2014 ฉันคิดว่าคุณตอบคำถามของคุณเองที่นั่น ในความเข้าใจของฉันมันเป็นวิธีที่เครื่องถูกออกแบบมาเพื่อทำงานในครั้งแรก
geekidharsh

40

~ พลิกบิตในค่า

ทำไม~2จะ-3มีการทำกับบิตว่าตัวเลขจะเป็นตัวแทนของ ตัวเลขจะแสดงเป็นส่วนเติมเต็มสอง

ดังนั้น 2 คือค่าไบนารี

00000010

และ ~ 2 พลิกบิตดังนั้นค่าตอนนี้:

11111101

ซึ่งเป็นตัวแทนไบนารีของ -3


2
ไม่ใช่ 11111101 == 253 ทศนิยม -3?
AKS

10
ขึ้นอยู่กับว่ามันหมายถึงจำนวนเต็มที่ลงนามหรือไม่ได้ลงนาม
driis

18

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

สิ่งหนึ่งที่จะเพิ่มคือเหตุผลที่ใช้ส่วนประกอบสองอย่างนี้เพื่อให้การดำเนินการเกี่ยวกับจำนวนลบจะเหมือนกับในจำนวนบวก ให้คิดว่า-3เป็นตัวเลขที่3ควรเพิ่มเพื่อให้ได้ศูนย์และคุณจะเห็นว่าตัวเลขนี้คือ1101โปรดจำไว้ว่าการบวกเลขฐานสองนั้นเหมือนกับโรงเรียนประถม (ทศนิยม) นอกจากนี้เพียงคุณมีหนึ่งเมื่อคุณได้รับสองมากกว่า 10 .

 1101 +
 0011 // 3
    =
10000
    =
 0000 // lose carry bit because integers have a constant number of bits.

ดังนั้น1101คือ-3พลิกบิตที่คุณได้รับ0010ซึ่งเป็นสอง


8

การดำเนินการนี้เป็นส่วนเสริมไม่ใช่การปฏิเสธ

พิจารณาว่า ~ 0 = -1 และทำงานจากที่นั่น

อัลกอริทึมสำหรับการปฏิเสธคือ "ส่วนเพิ่ม"

เธอรู้รึเปล่า? นอกจากนี้ยังมี "ส่วนเสริม" ที่หมายเลขผกผันมีความสมมาตรและมีทั้ง 0 และ a -0


6

ฉันรู้ว่าคำตอบสำหรับคำถามนี้โพสต์มานาน แต่ฉันต้องการแบ่งปันคำตอบของฉันเหมือนกัน

สำหรับการหาส่วนประกอบของตัวเลขอันดับแรกให้หาเลขฐานสองที่เทียบเท่ากัน ที่นี่ตัวเลขทศนิยม2จะแสดงเป็น0000 0010ในรูปแบบไบนารี ตอนนี้รับส่วนเสริมของมันด้วยการแปลงกลับ (หมุน 1 ทั้งหมดเป็น 0 และ 0 ทั้งหมดเป็น 1) ตัวเลขทั้งหมดของการแทนเลขฐานสองของมันซึ่งจะส่งผลให้:

0000 0010 → 1111 1101

นี่คือส่วนเติมเต็มของเลขทศนิยม 2 และตั้งแต่บิตแรกนั่นคือเครื่องหมายบิตคือ 1 ในเลขฐานสองหมายความว่าเครื่องหมายเป็นลบสำหรับหมายเลขที่เก็บไว้ (ที่นี่จำนวนที่อ้างถึงไม่ใช่ 2 แต่เป็นส่วนประกอบที่สมบูรณ์ของ 2)

ตอนนี้เนื่องจากตัวเลขจะถูกเก็บไว้เป็นส่วนเติมเต็มของ 2 (นำส่วนบวกของตัวเลขบวกหนึ่ง) มาแสดงแทนเลขฐานสองนี้1111 1101เป็นทศนิยมก่อนอื่นเราต้องหาส่วนประกอบของ 2 ซึ่งจะเป็น:

1111 1101 → 0000 0010 + 1 → 0000 0011

นี่คือส่วนประกอบ 2 ของ แสดงทศนิยมของเลขฐานสองที่เป็น0000 0011 และเนื่องจากบิตเครื่องหมายเป็นหนึ่งดังกล่าวข้างต้นจึงเป็นคำตอบที่ทำให้เกิดเป็น3-3

คำแนะนำ:หากคุณอ่านขั้นตอนนี้อย่างระมัดระวังคุณจะสังเกตได้ว่าผลลัพธ์สำหรับผู้ประกอบการที่สมบูรณ์นั้นจริง ๆ แล้วจำนวน (ตัวถูกดำเนินการ - ซึ่งใช้ตัวดำเนินการนี้) บวกกับเครื่องหมายลบ คุณสามารถลองกับตัวเลขอื่น ๆ ได้เช่นกัน


ทำไมมันเพิ่มสองครั้ง? ฉันเห็นadd, flip, addแล้ว 0010-> 0011-> 1100->1101
Braden ยอดเยี่ยม

1
มันพลิกพลิกเพิ่ม พลิกแรกสำหรับส่วนเสริม 1 และเนื่องจากมันถูกเก็บไว้ในส่วนเสริม 2 ของในระบบเมื่อคุณต้องการแสดงหมายเลขมันจะแสดงส่วนเติมเต็ม 2 ของหมายเลขที่จัดเก็บ (เช่นพลิกครั้งที่สองและเพิ่ม)
Himanshu Aggarwal

แต่จะไม่พลิก (พลิก (2)) เพียงแค่ 2? 0010 1101 0010
Braden ที่ดีที่สุด

ใช่มันจะเป็น 2 เท่านั้น แต่เนื่องจากเมื่อบิตถูกเก็บไว้ในหน่วยความจำบิตที่สำคัญที่สุดคือ 1 ซึ่งจะทำให้จำนวนลบในภายหลังตามที่อธิบายไว้ในคำตอบข้างต้น
Himanshu Aggarwal

1
จากสิ่งที่คุณกำลังอธิบายและทุกอย่างที่ฉันได้ทำการวิจัยนี้ไม่ได้เป็นส่วนประกอบสองอย่าง แต่เป็นส่วนประกอบ "ปกติ" หรือไม่ใช่ระดับบิต ในตรรกะและNOT 0 = 1 NOT 1 = 0ในระบบสี่บิตNOT 0011(3) = 1100(12 ไม่ได้ลงนาม -4 ลงนาม) จากสิ่งที่ฉันเข้าใจส่วนประกอบของสองถูกกำหนดเป็น(NOT n) + 1และใช้เพื่อค้นหาค่าลบของตัวเลขโดยไม่คำนึงถึงจำนวนบิต ดังนั้น2c(5) = -5. ดูสิตอนนี้มันสมเหตุสมผลดี ตราบใดที่คุณเรียกการดำเนินการนี้มันคืออะไร: bitwise ไม่
Braden สุดยอด

4

int a = 4; System.out.println (~ ก); ผลลัพธ์จะเป็น: -5

'~' ของจำนวนเต็มใด ๆ ใน java หมายถึงส่วนเติมเต็มของหมายเลข 1 เช่นฉันกำลัง ~ 4 ซึ่งหมายถึงในการเป็นตัวแทนไบนารี 0100 ครั้งแรกความยาวของจำนวนเต็มสี่ไบต์คือ 4 * 8 (8 บิตสำหรับ 1 ไบต์) = 32 ดังนั้นในหน่วยความจำระบบ 4 จะแสดงเป็น 0000 0000 0000 0000 0000 0000 0000 0100 ตอนนี้ ~ โอเปอเรเตอร์จะแสดงส่วนเสริม 1 ของเลขฐานสองด้านบน

เช่น 1111 1111 1111 1111 1111 1111 1111 1011-> 1 เติมเต็มบิตที่สำคัญที่สุดแสดงถึงสัญญาณของ no (ทั้ง - หรือ +) ถ้ามันเป็น 1 แล้วเครื่องหมายคือ '-' ถ้ามันเป็น 0 แล้วลงชื่อเป็น '+' ตาม ผลลัพธ์ของเรานี้คือจำนวนลบในจาวาตัวเลขลบจะถูกจัดเก็บในรูปแบบของ 2 ผลที่ได้มาเราต้องแปลงให้เป็น 2 ส่วน ทั้งหมดจะกลายเป็นศูนย์ยกเว้นบิตที่สำคัญที่สุด 1 (ซึ่งเป็นเครื่องหมายของเราแทนจำนวนซึ่งหมายความว่าเหลือ 31 บิต 1111 1111 1111 1111 1111 1111 1111 1111 1011 (ผลที่ได้มาจากผู้ประกอบการ ~) 1,000 0000 0000 0000 0000 0000 0000 0100 (ส่วนประกอบ 1)

1 (ส่วนประกอบ 2 ของ)

1,000 0000 0000 0000 0000 0000 0000 0101 ตอนนี้ผลลัพธ์คือ -5 ลองดูลิงค์นี้สำหรับวิดีโอ <[ตัวดำเนินการ bit ที่ชาญฉลาดในภาษาจาวา] https://youtu.be/w4pJ4cGWe9Y


2

เพียง ...........

ในฐานะที่เป็น 2 ส่วนประกอบของจำนวนใด ๆ ที่เราสามารถคำนวณได้โดย inverting 1s ถึง 0 ทั้งหมดและในทางกลับกันมากกว่าที่เราเพิ่ม 1 ลงไป ..

ที่นี่ N = ~ N ให้ผลลัพธ์ - (N + 1) เสมอ เนื่องจากระบบจัดเก็บข้อมูลในรูปแบบของส่วนประกอบ 2 ซึ่งหมายความว่าจะเก็บ ~ N เช่นนี้

  ~N = -(~(~N)+1) =-(N+1). 

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

  N = 10  = 1010
  Than ~N  = 0101
  so ~(~N) = 1010
  so ~(~N) +1 = 1011 

ตอนนี้จุดมาจากที่ลบมา ความคิดเห็นของฉันสมมติว่าเรามีการลงทะเบียน 32 บิตซึ่งหมายถึง 2 ^ 31 -1 บิตที่เกี่ยวข้องในการดำเนินการและส่วนที่เหลือหนึ่งบิตซึ่งการเปลี่ยนแปลงในการคำนวณก่อนหน้านี้ (ส่วนประกอบ) เก็บไว้เป็นบิตสัญญาณซึ่งเป็น 1 ปกติ และเราได้ผลลัพธ์เป็น ~ 10 = -11

~ (-11) = 10;

ด้านบนเป็นจริงถ้า printf ("% d", ~ 0); เราได้รับผล: -1;

แต่ printf ("% u", ~ 0) มากกว่าผลลัพธ์: 4294967295 บนเครื่อง 32 บิต


1

ตัวดำเนินการเสริม Bitwise (~) เป็นตัวดำเนินการเอก

มันทำงานตามวิธีการดังต่อไปนี้

ก่อนอื่นมันจะแปลงจำนวนทศนิยมที่กำหนดให้ เป็นค่าเลขฐานสองที่สอดคล้องกันในกรณีที่ 2 จะเป็นการแปลง 2 ถึง 0000 0010 (เป็นเลขฐานสอง 8 บิต)

จากนั้นมันจะแปลง 1 ทั้งหมดในจำนวนเป็น 0 และศูนย์ทั้งหมดเป็น 1 จากนั้นตัวเลขจะกลายเป็น 1111 1101

นั่นคือส่วนประกอบ 2 ของ -3

เพื่อหาค่าที่ไม่ได้ลงนามโดยใช้ส่วนประกอบเช่นเพียงแปลง 1111 1101 เป็นทศนิยม (= 4294967293) เราสามารถใช้% u ระหว่างการพิมพ์ได้


1

ฉันคิดว่าสำหรับคนส่วนใหญ่ความสับสนนั้นมาจากความแตกต่างระหว่างเลขฐานสิบและเลขฐานสองที่เซ็นชื่อดังนั้นให้ชี้แจงก่อน:

สำหรับโลกเลขฐานสิบของมนุษย์: 01 หมายถึง 1, -01 หมายถึง -1, สำหรับโลกฐานสองของคอมพิวเตอร์: 101 หมายถึง 5 หากไม่ได้ลงนาม 101 หมายถึง (-4 + 1) ถ้าลงชื่อในขณะที่ตัวเลขที่เซ็นชื่ออยู่ที่ตำแหน่ง x | x

ดังนั้นบิตที่ 2 ของพลิก = ~ 2 = ~ (010) = 101 = -4 + 1 = -3 ความสับสนเกิดขึ้นจากการผสมผลลัพธ์ที่ลงนาม (101 = -3) และผลลัพธ์ที่ยังไม่ได้อ่าน (101 = 5)


1

tl; dr ~พลิกบิต เป็นผลให้การเปลี่ยนแปลงสัญญาณ ~2เป็นจำนวนลบ ( 0b..101) เพื่อส่งออกจำนวนลบrubyพิมพ์-แล้วเติมเต็มสองของ:~2 -(~~2 + 1) == -(2 + 1) == 3ตัวเลขที่เป็นบวกจะถูกส่งออกตามที่เป็นอยู่

มีค่าภายในและแสดงสตริง สำหรับจำนวนเต็มบวกโดยทั่วไปแล้วพวกเขาตรง:

irb(main):001:0> '%i' % 2
=> "2"
irb(main):002:0> 2
=> 2

หลังถูกเทียบเท่ากับ:

irb(main):003:0> 2.to_s
"2"

~พลิกบิตของค่าภายใน เป็น2 เป็น สองจุด ( ) แทนจำนวนอนันต์ของ's เนื่องจากบิตที่สำคัญที่สุด (MSB) ของผลลัพธ์คือผลลัพธ์จึงเป็นจำนวนลบ ( ) หากต้องการส่งออกจำนวนพิมพ์เชิงลบจากนั้นให้ส่วนประกอบทั้งสองของค่าภายใน เติมเต็มทั้งสองจะถูกคำนวณโดยการพลิกบิตแล้วเพิ่ม ส่วนประกอบที่สองของมี เช่นนี้:0b010~20b..101..11(~2).negative? == trueruby-10b..1013

irb(main):005:0> '%b' % 2
=> "10"
irb(main):006:0> '%b' % ~2
=> "..101"
irb(main):007:0> ~2
=> -3

เมื่อต้องการหาผลรวมมันจะพลิกบิตซึ่งเปลี่ยนเครื่องหมาย หากต้องการส่งออกจำนวนลบจะพิมพ์-จากนั้น~~2 + 1( ~~2 == 2)

เหตุผลที่rubyส่งออกตัวเลขติดลบเช่นนั้นเป็นเพราะมันปฏิบัติต่อค่าที่เก็บไว้เป็นส่วนเติมเต็มของค่าสัมบูรณ์ ในคำอื่น ๆ 0b..101สิ่งที่เก็บไว้ xมันเป็นจำนวนลบและเป็นเช่นนั้นเป็นส่วนประกอบที่สองของค่าบางอย่าง ในการค้นหาxมันเป็นการรวมกันของ0b..101สองอย่าง xซึ่งเป็นส่วนประกอบที่สองของส่วนประกอบที่สองของ อันไหนx(เช่น~(~2 + 1) + 1 == 2)

ในกรณีที่คุณใช้~กับจำนวนลบมันจะพลิกบิต (ซึ่งยังคงเปลี่ยนเครื่องหมาย):

irb(main):008:0> '%b' % -3
=> "..101"
irb(main):009:0> '%b' % ~-3
=> "10"
irb(main):010:0> ~-3
=> 2

สิ่งที่สับสนมากกว่านั้นคือ~0xffffff00 != 0xff(หรือค่าอื่น ๆ ที่มี MSB เท่ากับ1) ~0xf0 != 0x0fลองลดความซับซ้อนของมันบิต: นั่นเป็นเพราะมันถือว่า0xf0เป็นจำนวนบวก ซึ่งทำให้รู้สึกจริง ดังนั้น~0xf0 == 0x..f0f. ผลลัพธ์คือจำนวนลบ ส่วนประกอบที่สองของมี0x..f0f 0xf1ดังนั้น:

irb(main):011:0> '%x' % ~0xf0
=> "..f0f"
irb(main):012:0> (~0xf0).to_s(16)
=> "-f1"

ในกรณีที่คุณจะไม่ใช้ตัวดำเนินการระดับบิตกับผลลัพธ์คุณสามารถพิจารณา~เป็นตัว-x - 1ดำเนินการ:

irb(main):018:0> -2 - 1
=> -3
irb(main):019:0> --3 - 1
=> 2

แต่นั่นก็เป็นเนื้อหาที่ไม่ค่อยได้ใช้

ตัวอย่างการพูด Let 's คุณได้รับ 8 บิต (สำหรับความเรียบง่าย) netmask และคุณต้องการในการคำนวณจำนวนของ0's คุณสามารถคำนวณได้โดยการพลิกบิตและการโทรbit_length( 0x0f.bit_length == 4) แต่~0xf0 == 0x..f0fดังนั้นเราต้องตัดบิตที่ไม่จำเป็นออก:

irb(main):014:0> '%x' % (~0xf0 & 0xff)
=> "f"
irb(main):015:0> (~0xf0 & 0xff).bit_length
=> 4

หรือคุณสามารถใช้ตัวดำเนินการ XOR ( ^):

irb(main):016:0> i = 0xf0
irb(main):017:0> '%x' % i ^ ((1 << i.bit_length) - 1)
=> "f"

0

ก่อนอื่นเราต้องแยกตัวเลขที่กำหนดให้เป็นเลขฐานสองของมันแล้วย้อนกลับโดยการเพิ่มที่เลขฐานสองสุดท้ายหลังจากการดำเนินการนี้เราจะต้องให้เครื่องหมายตรงข้ามกับตัวเลขก่อนหน้านี้ที่เรากำลังหาผู้ร้องเรียน ~ 2 = -3 คำอธิบาย : 2s รูปแบบไบนารีคือ 00000010 เปลี่ยนเป็น 11111101 นี่คือส่วนประกอบที่สมบูรณ์จากนั้นจึง 00000010 + 1 = 00000011 ซึ่งเป็นรูปแบบไบนารีของสามและด้วย -sign Ie, -3


0

ตัวดำเนินการ bit-wise เป็นตัวดำเนินการเอกที่ทำงานบนวิธีการเซ็นชื่อและขนาดตามประสบการณ์และความรู้ของฉัน

ตัวอย่างเช่น ~ 2 จะส่งผลให้ -3

นี่เป็นเพราะตัวดำเนินการ bit-wise จะแสดงตัวเลขในเครื่องหมายและขนาดซึ่งเป็น 0000 0010 (ตัวดำเนินการ 8 บิต) ก่อนซึ่ง MSB เป็นบิตสัญญาณ

จากนั้นต่อมามันก็หาจำนวนลบของ 2 ซึ่งก็คือ -2

-2 แสดงเป็น 1,000 0010 (ตัวดำเนินการ 8 บิต) ในเครื่องหมายและขนาด

ต่อมามันเพิ่ม 1 เข้ากับ LSB (1,000 0010 + 1) ซึ่งให้คุณ 1,000 0011

ซึ่งคือ -3


0

Javascript tilde (~) รวมค่าที่กำหนดเข้ากับส่วนเสริม - บิตทั้งหมดกลับด้าน นั่นคือตัวหนอนทั้งหมด มันไม่ได้ลงนามความเห็น มันไม่ได้เพิ่มหรือลบปริมาณใด ๆ

0 -> 1
1 -> 0
...in every bit position [0...integer nbr of bits - 1]

สำหรับโปรเซสเซอร์เดสก์ท็อปมาตรฐานที่ใช้ภาษาระดับสูงเช่น JavaScript เลขฐานสองที่ลงนามเป็นพื้นฐานที่สุด แต่จำไว้เสมอว่าไม่ใช่ประเภทเดียว บิตที่ระดับ CPU อาจมีการตีความตามปัจจัยหลายประการ ที่ระดับ 'รหัส' ในกรณีนี้ JavaScript พวกเขาถูกตีความว่าเป็นจำนวนเต็ม 32 บิตที่มีลายเซ็นตามคำจำกัดความ (ปล่อยให้ลอยออกจากสิ่งนี้) คิดว่ามันเป็นควอนตัม 32 บิตเหล่านั้นเป็นตัวแทนของค่าที่เป็นไปได้มากมายในครั้งเดียว ขึ้นอยู่กับเลนส์แปลงที่คุณมองผ่าน

JavaScript Tilde operation (1's complement)

BASE2 lens
~0001 -> 1110  - end result of ~ bitwise operation

BASE10 Signed lens (typical JS implementation)
~1  -> -2 

BASE10 Unsigned lens 
~1  -> 14 

ทั้งหมดที่กล่าวมาเป็นจริงในเวลาเดียวกัน


0

การกระทำโดยทั่วไปเป็นส่วนประกอบไม่ใช่การปฏิเสธ

ที่นี่ x = ~ x ให้ผลลัพธ์ - (x + 1) เสมอ

x = ~ 2

- (2 + 1)

-3

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