(-2147483648> 0) ผลตอบแทนจริงใน C ++?


241

-2147483648 เป็นจำนวนเต็มที่น้อยที่สุดสำหรับประเภทจำนวนเต็มด้วย 32 บิต แต่ดูเหมือนว่าจะล้นในif(...)ประโยค:

if (-2147483648 > 0)
    std::cout << "true";
else
    std::cout << "false";

สิ่งนี้จะพิมพ์ออกมาtrueในการทดสอบของฉัน อย่างไรก็ตามหากเราส่ง -2147483648 ไปเป็นจำนวนเต็มผลลัพธ์จะแตกต่างกัน:

if (int(-2147483648) > 0)
    std::cout << "true";
else
    std::cout << "false";

falseนี้จะพิมพ์

ฉันสับสน ใครสามารถให้คำอธิบายเกี่ยวกับเรื่องนี้?


อัปเดต 02-05-2012:

ขอบคุณสำหรับความคิดเห็นของคุณในคอมไพเลอร์ของฉันขนาดของ int คือ 4 ไบต์ ฉันใช้ VC เพื่อทดสอบอย่างง่าย ฉันเปลี่ยนคำอธิบายในคำถามของฉัน

นั่นเป็นคำตอบที่ดีมากในโพสต์นี้AndreyTให้คำอธิบายโดยละเอียดเกี่ยวกับวิธีคอมไพเลอร์จะทำงานกับอินพุตเหล่านี้อย่างไรและวิธีการใช้จำนวนเต็มขั้นต่ำนี้ qPCR4virในอีกทางหนึ่งให้ "ความอยากรู้" ที่เกี่ยวข้องและวิธีการแสดงจำนวนเต็ม น่าประทับใจมาก!


48
"เราทุกคนรู้ว่า -2147483648 เป็นจำนวนเต็มที่น้อยที่สุด"ซึ่งขึ้นอยู่กับขนาดของจำนวนเต็ม
orlp

14
"เราทุกคนรู้ว่า -2147483648 เป็นจำนวนเต็มน้อยที่สุด" - ฉันคิดว่าไม่มีจำนวนเต็มที่น้อยที่สุดเนื่องจากมีจำนวนไม่มากนัก ... ไม่ว่าอะไรก็ตาม

@Inisheer ด้วยจำนวนเต็ม 4 ไบต์คุณอาจมี a INT_MINของ-9223372036854775808ถ้าCHAR_BITเป็น 16 และแม้จะมีCHAR_BIT == 8และsizeof(int== 4) `คุณอาจได้รับ-9223372036854775807เพราะ C ไม่ต้องใช้หมายเลข 2 เสริม
12431234123412341234123

คำตอบ:


391

-2147483648ไม่ใช่ "หมายเลข" ภาษา C ++ ไม่สนับสนุนค่าตัวอักษรเชิงลบ

-2147483648อันที่จริงแล้วเป็นนิพจน์: ค่าตัวอักษรเชิงบวกที่มีตัว2147483648ดำเนิน-การunary อยู่ด้านหน้า 2147483648เห็นได้ชัดว่ามีค่ามากเกินไปสำหรับด้านบวกของintช่วงบนแพลตฟอร์มของคุณ ถ้าชนิดlong intมีหลากหลายมากขึ้นบนแพลตฟอร์มของคอมไพเลอร์จะต้องถือว่าโดยอัตโนมัติที่2147483648มีlong intประเภท (ใน C ++ 11 คอมไพเลอร์จะต้องพิจารณาlong long intประเภทด้วย) สิ่งนี้จะทำให้คอมไพเลอร์ประเมินผล-2147483648ในโดเมนที่มีขนาดใหญ่กว่าและผลลัพธ์จะเป็นลบตามที่เราคาดหวัง

อย่างไรก็ตามในกรณีของคุณช่วงของช่วงlong intนั้นเหมือนกับช่วงintโดยทั่วไปไม่มีประเภทจำนวนเต็มที่มีช่วงมากกว่าintบนแพลตฟอร์มของคุณ นี่หมายถึงอย่างเป็นทางการว่าค่าคงที่ในเชิงบวกจะ2147483648โอเวอร์โฟลว์จำนวนเต็มที่มีลายเซ็นทั้งหมดซึ่งหมายความว่าพฤติกรรมของโปรแกรมของคุณไม่ได้ถูกกำหนด (เป็นเรื่องแปลกที่ข้อกำหนดภาษาเลือกใช้พฤติกรรมที่ไม่ได้กำหนดในกรณีเช่นนั้นแทนที่จะต้องใช้ข้อความวินิจฉัย แต่นั่นเป็นวิธีที่มันเป็น)

ในทางปฏิบัติโดยคำนึงถึงพฤติกรรมที่ไม่ได้กำหนด2147483648อาจได้รับการตีความว่าเป็นค่าลบที่ขึ้นอยู่กับการใช้งานบางอย่างซึ่งจะเปลี่ยนเป็นบวกหลังจากที่-นำไปใช้กับunary อีกทางหนึ่งการใช้งานบางอย่างอาจตัดสินใจใช้ชนิดที่ไม่ได้ลงนามเพื่อแทนค่า (ตัวอย่างเช่นในคอมไพเลอร์ C89 / 90 จำเป็นต้องใช้unsigned long intแต่ไม่ใช่ใน C99 หรือ C ++) การใช้งานได้รับอนุญาตให้ทำอะไรก็ได้เนื่องจากพฤติกรรมไม่ได้กำหนดไว้แล้ว

นี่คือเหตุผลว่าทำไมค่าคงที่เช่นINT_MINนั้นมักถูกกำหนดเป็น

#define INT_MIN (-2147483647 - 1)

แทนที่จะดูตรงไปตรงมามากกว่า

#define INT_MIN -2147483648

หลังจะไม่ทำงานตามที่ตั้งใจไว้


78
#define INT_MIN (-2147483647 - 1)และนี่ก็เป็นเหตุผลที่นี้จะทำ:
orlp

5
@ RichardJ.RossIII - ด้วยเสียงดังกราวคุณอาจได้รับตัวอักษรแบบ 64 บิตเนื่องจากมันใหญ่เกินกว่าจะใส่เข้าไปintได้ การใช้งานของ OP อาจไม่มีประเภท 64 บิต
Carl Norum

1
@ RichardJ.RossIII: ฉันเชื่อว่าพฤติกรรมนี้เป็นการนำไปปฏิบัติ / ไม่ได้กำหนด
Oliver Charlesworth

3
ฉันไม่เคยคิดเลยว่า "จำนวนลบ" จะไม่ถูกแยกวิเคราะห์เช่นนี้ ฉันไม่เห็นเหตุผล ฉันหวังว่า-1.0จะถูกแยกวิเคราะห์เป็นค่าลบสองเท่าใช่หรือไม่
leemes

6
@ qPCR4vir: ไม่อย่างที่ฉันเขียนไว้ในความคิดเห็นของฉันกับคำตอบของคุณทั้ง C สมัยใหม่และ C ++ ไม่อนุญาตให้ใช้ประเภทที่ไม่ได้ลงชื่อในกรณีนี้ (ที่มีค่าคงที่ทศนิยมที่ไม่ได้ใส่ ) เฉพาะมาตรฐาน C (C89 / 90) แรกที่อนุญาตunsigned long intในบริบทนี้ แต่ใน C99 การอนุญาตนี้ถูกลบ ต้องไม่มีตัวอักษรผสมใน C และ C ++ เพื่อให้มีประเภทที่เซ็นชื่อ หากคุณเห็นประเภทที่ไม่ได้ลงชื่อที่นี่เมื่อผู้เซ็นชื่อใช้งานได้แสดงว่าคอมไพเลอร์ของคุณเสีย หากคุณเห็นประเภทที่ไม่ได้ลงชื่อที่นี่เมื่อไม่มีประเภทที่เซ็นชื่อจะทำงานได้นี่เป็นเพียงการแสดงเฉพาะของพฤติกรรมที่ไม่ได้กำหนด
AnT

43

คอมไพเลอร์ (VC2012) เลื่อนระดับเป็นจำนวนเต็ม "ขั้นต่ำ" ที่สามารถเก็บค่าได้ ในกรณีแรกsigned int(และlong int) ไม่สามารถ (ก่อนที่จะใช้สัญญาณ) แต่unsigned intสามารถ: 2147483648มีunsigned int ???? ชนิด ในครั้งที่สองคุณบังคับจากintunsigned

const bool i= (-2147483648 > 0) ;  //   --> true

คำเตือน C4146: ตัวดำเนินการลบพร้อมกับประเภทที่ไม่ได้ลงนามผลยังคงไม่ได้ลงนาม

นี่คือ "ความอยากรู้" ที่เกี่ยวข้อง:

const bool b= (-2147483647      > 0) ; //  false
const bool i= (-2147483648      > 0) ; //  true : result still unsigned
const bool c= ( INT_MIN-1       > 0) ; //  true :'-' int constant overflow
const bool f= ( 2147483647      > 0) ; //  true
const bool g= ( 2147483648      > 0) ; //  true
const bool d= ( INT_MAX+1       > 0) ; //  false:'+' int constant overflow
const bool j= ( int(-2147483648)> 0) ; //  false : 
const bool h= ( int(2147483648) > 0) ; //  false
const bool m= (-2147483648L     > 0) ; //  true 
const bool o= (-2147483648LL    > 0) ; //  false

มาตรฐาน C ++ 11 :

2.14.2 ตัวอักษรจำนวนเต็ม [lex.icon]

...

ตัวอักษรจำนวนเต็มเป็นลำดับของตัวเลขที่ไม่มีส่วนใดส่วนหนึ่งหรือเลขชี้กำลัง ตัวอักษรจำนวนเต็มอาจมีคำนำหน้าที่ระบุฐานและคำต่อท้ายที่ระบุประเภทของมัน

...

ชนิดของตัวอักษรจำนวนเต็มเป็นรายการแรกของรายการที่สอดคล้องกันซึ่งสามารถแสดงค่าได้

ป้อนคำอธิบายรูปภาพที่นี่

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

และนี่คือกฎการส่งเสริมการขายสำหรับจำนวนเต็มในมาตรฐาน

4.5 การส่งเสริมการขายแบบรวม [conv.prom]

prvalue ของจำนวนเต็มชนิดอื่นที่ไม่ใช่bool, char16_t, char32_tหรือ wchar_tมีจำนวนเต็มแปลงยศ (4.13) น้อยกว่ายศ int ที่สามารถแปลงเป็น prvalue ของประเภทintถ้าintสามารถเป็นตัวแทนของค่าทั้งหมดของประเภทแหล่งที่มา มิฉะนั้น prvalue แหล่งที่สามารถแปลงเป็น prvalue unsigned intของประเภท


3
@ qPCR4vir: ใน C89 / 90 คอมไพเลอร์ที่ถูกควรจะใช้ประเภทint, long int, unsigned long intเพื่อเป็นตัวแทนของค่าคงทศนิยม unsuffixed นั่นเป็นภาษาเดียวที่อนุญาตให้ใช้ประเภทที่ไม่ได้ลงชื่อสำหรับค่าคงที่ทศนิยมที่ไม่ได้ใส่ ใน C ++ 98 มันเป็นหรือint long intไม่อนุญาตประเภทที่ไม่ได้ลงชื่อ ทั้ง C (เริ่มจาก C99) และ C ++ ไม่อนุญาตให้คอมไพเลอร์ใช้ประเภทที่ไม่ได้ลงนามในบริบทนี้ แน่นอนว่าคอมไพเลอร์ของคุณมีอิสระที่จะใช้ประเภทที่ไม่ได้ลงชื่อถ้าไม่มีคนที่เซ็นชื่อใช้งานได้ แต่นี่ก็เป็นเพียงการแสดงเฉพาะของพฤติกรรมที่ไม่ได้กำหนด
AnT

@AndreyT ที่ดี! ความแข็งแกร่งของคุณ VC2012 หักหรือไม่
qPCR4vir

@ qPCR4vir: AFAIK, VC2012 ไม่ได้เป็น C ++ 11 คอมไพเลอร์เลย (มันคืออะไร?) ซึ่งหมายความว่ามันมีการใช้อย่างใดอย่างหนึ่งintหรือจะเป็นตัวแทนของlong int 2147483648นอกจากนี้ AFAIK ใน VC2012 ทั้งสองintและlong intเป็นประเภท 32 บิต ซึ่งหมายความว่าใน VC2012 ที่แท้จริง2147483648จะนำไปสู่พฤติกรรมที่ไม่ได้กำหนด เมื่อพฤติกรรมไม่ได้กำหนดคอมไพเลอร์ได้รับอนุญาตให้ทำอะไร นั่นหมายความว่า VC2012 ไม่แตกหัก มันออกข้อความวินิจฉัยที่ทำให้เข้าใจผิด แทนที่จะบอกคุณว่าพฤติกรรมนั้นไม่ได้ถูกกำหนดออกมา แต่ก็ตัดสินใจที่จะใช้ประเภทที่ไม่ได้ลงนาม
AnT

@AndreyT: คุณกำลังบอกว่าคอมไพเลอร์มีอิสระที่จะปล่อยปีศาจจมูกได้หรือไม่ถ้าซอร์สโค้ดมีทศนิยมตามตัวอักษรที่ไม่ได้ใส่ค่าซึ่งเกินค่าสูงสุดของเครื่องหมายlongและไม่จำเป็นต้องมีการวินิจฉัย? ดูเหมือนว่าจะหัก
supercat

"เตือน C4146" เดียวกันใน VS2008 และ "ค่าคงที่ทศนิยมนี้ไม่ได้ลงนามเฉพาะใน ISO C90" ใน G ++
spyder

6

ในระยะสั้น2147483648ล้นไป-2147483648และเป็น(-(-2147483648) > 0)true

นี่คือ2147483648ลักษณะที่ปรากฏในไบนารี

นอกจากนี้ในกรณีของการคำนวณไบนารีที่ลงชื่อบิตที่สำคัญที่สุด ("MSB") คือบิตเครื่องหมาย คำถามนี้อาจช่วยอธิบายได้ว่าทำไม


4

เพราะ-2147483648เป็นจริง2147483648ด้วยการปฏิเสธ (- )ตัวเลขไม่ใช่สิ่งที่คุณคาดหวัง จริงๆแล้วมันเทียบเท่ากับ pseudocode นี้:operator -(2147483648)

ทีนี้สมมติว่าคอมไพเลอร์ของคุณมีsizeof(int)ค่าเท่ากับ4และCHAR_BITถูกกำหนดเป็น8ซึ่งจะทำให้2147483648ค่าที่ลงนามสูงสุดของจำนวนเต็มล้นจนล้น (2147483647 )แล้วค่าบวกสูงสุดคืออะไร? ให้ผลเป็นจำนวนเต็ม 4 บิตและ 2 วินาที

รอ! 8 ล้นจำนวนเต็ม! พวกเราทำอะไร? ใช้การเป็นตัวแทนที่ไม่ได้ลงนาม1000และตีความบิตเป็นจำนวนเต็มที่ลงนามแล้ว การแสดงนี้ทำให้เรามี-8ถูกใช้ 2s ปฏิเสธสมบูรณ์ที่เกิดในที่ที่เราทุกคนรู้ว่ามีค่ามากกว่า80

นี่คือสาเหตุที่<limits.h>(และ<climits>) โดยทั่วไปกำหนดINT_MINเป็น((-2147483647) - 1)- เพื่อให้จำนวนเต็มที่มีลายเซ็นสูงสุด ( 0x7FFFFFFF) ถูกทำให้ไร้ผล ( 0x80000001), แล้วลดค่า ( 0x80000000)


สำหรับหมายเลข 4 บิตการปฏิเสธอย่างสมบูรณ์ของทั้งสอง-8ก็ยังคง-8อยู่
Ben Voigt

ยกเว้นว่า -8 ถูกตีความว่าเป็น 0-8 ไม่ใช่ลบ 8 และ 8 โอเวอร์โฟลว์มีการลงนาม 4 บิต
โคลจอห์นสัน

พิจารณา-(8)ว่าใน C ++ เหมือนกับอะไร-8- มันเป็นการลบล้างที่ใช้กับตัวอักษรไม่ใช่ตัวอักษรเชิงลบ ตัวอักษรคือ8ซึ่งไม่พอดีกับจำนวนเต็ม 4 บิตที่มีลายเซ็นดังนั้นจึงต้องไม่ได้ลงนาม 1000รูปแบบคือ จนถึงคำตอบของคุณถูกต้อง การปฏิเสธโดยสมบูรณ์ของทั้งสอง1000ใน 4 บิตคือ1000ไม่สำคัญว่าจะมีการลงชื่อหรือไม่ได้ลงชื่อ คำตอบของคุณบอกว่า "ตีความบิตเป็นจำนวนเต็มที่ลงนาม" ซึ่งทำให้ค่า-8หลังจากการลบล้างทั้งสองอย่างเช่นเดียวกับก่อนการปฏิเสธ
Ben Voigt

แน่นอนใน "4 บิต C ++" ไม่มี "ตีความบิตเป็นขั้นตอนจำนวนเต็มที่ลงนาม" ตัวหนังสือกลายเป็นชนิดที่เล็กที่สุดที่สามารถแสดงมันซึ่งเป็นไม่ได้ลงนาม 4 บิตจำนวนเต็ม 8มูลค่าของที่แท้จริงคือ ปฏิเสธถูกนำไปใช้ (โมดูโล 16) 8ส่งผลให้คำตอบสุดท้ายของ การเข้ารหัสยังคงเป็น 1,000 แต่ค่าจะแตกต่างกันเนื่องจากมีการเลือกประเภทที่ไม่ได้ลงชื่อ
Ben Voigt
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.