ฉันต้องการกำหนดฟังก์ชันที่รับunsigned int
อาร์กิวเมนต์เป็นและส่งคืนint
โมดูโลที่สอดคล้องกัน UINT_MAX + 1 ให้กับอาร์กิวเมนต์
ความพยายามครั้งแรกอาจมีลักษณะดังนี้:
int unsigned_to_signed(unsigned n)
{
return static_cast<int>(n);
}
แต่อย่างที่นักกฎหมายภาษาใด ๆ ทราบการคัดเลือกจากไม่ได้ลงนามเป็นเซ็นชื่อสำหรับค่าที่มีขนาดใหญ่กว่า INT_MAX นั้นถูกกำหนดให้ใช้งานได้
ฉันต้องการใช้สิ่งนี้ซึ่ง (ก) อาศัยเฉพาะพฤติกรรมที่กำหนดโดยข้อมูลจำเพาะเท่านั้น และ (b) รวบรวมเป็น no-op บนเครื่องที่ทันสมัยและปรับแต่งคอมไพเลอร์
สำหรับเครื่องจักรที่แปลกประหลาด ... หากไม่มีโมดูโลคอนดักเตอร์ int ที่ลงนาม UINT_MAX + 1 ไปยัง int ที่ไม่ได้ลงนามสมมติว่าฉันต้องการยกเว้น ถ้ามีมากกว่าหนึ่ง (ฉันไม่แน่ใจว่าเป็นไปได้) สมมติว่าฉันต้องการอันที่ใหญ่ที่สุด
ตกลงครั้งที่สอง:
int unsigned_to_signed(unsigned n)
{
int int_n = static_cast<int>(n);
if (n == static_cast<unsigned>(int_n))
return int_n;
// else do something long and complicated
}
ฉันไม่ได้สนใจเกี่ยวกับประสิทธิภาพมากนักเมื่อฉันไม่ได้ใช้ระบบเสริมสองแบบทั่วไปเนื่องจากในความเห็นที่ต่ำต้อยของฉันซึ่งไม่น่าเป็นไปได้ และถ้ารหัสของฉันกลายเป็นปัญหาคอขวดในระบบขนาดเครื่องหมายที่อยู่ทั่วไปในปี 2050 ฉันพนันได้เลยว่าใครบางคนสามารถคิดออกและปรับให้เหมาะสมได้
ตอนนี้ความพยายามครั้งที่สองใกล้เคียงกับที่ฉันต้องการ แม้ว่าการแคสต์ถึงจะint
ถูกกำหนดให้ใช้งานสำหรับอินพุตบางตัว แต่การส่งกลับไปยังunsigned
ได้รับการรับรองตามมาตรฐานเพื่อรักษาค่าโมดูโล UINT_MAX + 1 ดังนั้นเงื่อนไขจะตรวจสอบสิ่งที่ฉันต้องการและมันจะรวมเข้ากับระบบใด ๆ ที่ฉันน่าจะพบ
อย่างไรก็ตาม ... ฉันยังคงแคสต์int
โดยไม่ได้ตรวจสอบก่อนว่าจะเรียกใช้พฤติกรรมที่กำหนดการนำไปใช้หรือไม่ ในระบบสมมุติบางอย่างในปี 2050 มันสามารถทำใครรู้อะไรได้ สมมติว่าฉันต้องการหลีกเลี่ยงสิ่งนั้น
คำถาม: "ความพยายามครั้งที่สาม" ของฉันควรมีลักษณะอย่างไร
ในการสรุปฉันต้องการ:
- ส่งจาก int ที่ไม่ได้ลงนามเป็น int ที่ลงชื่อ
- เก็บค่า mod UINT_MAX + 1
- เรียกใช้เฉพาะพฤติกรรมที่ได้รับคำสั่งมาตรฐาน
- คอมไพล์เป็น no-op บนเครื่อง twos-complement ทั่วไปด้วยการปรับแต่งคอมไพเลอร์
[อัปเดต]
ให้ฉันยกตัวอย่างเพื่อแสดงว่าเหตุใดจึงไม่ใช่คำถามที่ไม่สำคัญ
พิจารณาการใช้งาน C ++ สมมุติด้วยคุณสมบัติต่อไปนี้:
sizeof(int)
เท่ากับ 4sizeof(unsigned)
เท่ากับ 4INT_MAX
เท่ากับ 32767INT_MIN
เท่ากับ -2 32 + 32768UINT_MAX
เท่ากับ 2 32 - 1- เลขคณิตบน
int
คือโมดูโล 2 32 (เข้าสู่ช่วงINT_MIN
ถึงINT_MAX
) std::numeric_limits<int>::is_modulo
เป็นความจริง- หล่อที่ไม่มีการลงชื่อ
n
เพื่อรักษา int ค่าสำหรับ 0 <= n <= 32767 และอัตราผลตอบแทนเป็นศูนย์มิฉะนั้น
ในการใช้งานสมมุตินี้มีint
ค่าหนึ่งที่สอดคล้องกัน (mod UINT_MAX + 1) สำหรับแต่ละunsigned
ค่า ดังนั้นคำถามของฉันจะถูกกำหนดไว้อย่างชัดเจน
ฉันอ้างว่าการใช้งาน C ++ สมมุตินี้สอดคล้องกับข้อกำหนด C ++ 98, C ++ 03 และ C ++ 11 อย่างสมบูรณ์ ฉันยอมรับว่าฉันไม่ได้จำทุกคำของพวกเขาทั้งหมด ... แต่ฉันเชื่อว่าฉันได้อ่านส่วนที่เกี่ยวข้องอย่างละเอียดแล้ว ดังนั้นหากคุณต้องการให้ฉันยอมรับคำตอบของคุณคุณต้อง (a) อ้างอิงข้อมูลจำเพาะที่กำหนดกฎเกณฑ์การใช้งานสมมุติฐานนี้หรือ (b) จัดการอย่างถูกต้อง
อันที่จริงคำตอบที่ถูกต้องจัดการทุกการดำเนินงานสมมุติได้รับอนุญาตตามมาตรฐาน นั่นคือสิ่งที่ "เรียกใช้เฉพาะพฤติกรรมที่ได้รับคำสั่งมาตรฐาน" ตามคำจำกัดความ
อนึ่งโปรดทราบว่าที่std::numeric_limits<int>::is_modulo
นี่ไม่มีประโยชน์ด้วยเหตุผลหลายประการ ประการหนึ่งอาจเป็นได้true
แม้ว่าการร่ายที่ไม่ได้ลงชื่อจะไม่สามารถใช้ได้กับค่าที่ไม่ได้ลงชื่อจำนวนมาก สำหรับอีกระบบหนึ่งอาจเป็นได้true
แม้กระทั่งในระบบส่วนเติมเต็มหรือขนาดเครื่องหมายถ้าเลขคณิตเป็นเพียงโมดูโลช่วงจำนวนเต็มทั้งหมด และอื่น ๆ ถ้าคำตอบของคุณขึ้นอยู่กับis_modulo
ว่ามันผิด
[อัปเดต 2]
คำตอบของ hvdสอนฉันบางอย่าง: การนำ C ++ สมมุติของฉันไปใช้กับจำนวนเต็มไม่ได้รับอนุญาตจาก C สมัยใหม่มาตรฐาน C99 และ C11 มีความเฉพาะเจาะจงมากเกี่ยวกับการแสดงจำนวนเต็มที่ลงนาม แท้จริงพวกเขาอนุญาตให้ใช้เฉพาะส่วนเสริมสองส่วนส่วนเติมเต็มและขนาดเครื่องหมาย (หัวข้อ 6.2.6.2 ย่อหน้า (2);)
แต่ C ++ ไม่ใช่ C ตามที่ปรากฎข้อเท็จจริงนี้อยู่ในหัวใจหลักของคำถามของฉัน
มาตรฐาน C ++ 98 ดั้งเดิมอ้างอิงจาก C89 ที่เก่ากว่ามากซึ่งระบุว่า (หัวข้อ 3.1.2.5):
สำหรับประเภทจำนวนเต็มที่ลงนามแต่ละประเภทจะมีประเภทจำนวนเต็มไม่ได้ลงนามที่ตรงกัน (แต่ต่างกัน) (กำหนดด้วยคีย์เวิร์ดที่ไม่ได้ลงนาม) ซึ่งใช้พื้นที่เก็บข้อมูลเท่ากัน (รวมถึงข้อมูลเครื่องหมาย) และมีข้อกำหนดการจัดตำแหน่งเดียวกัน ช่วงของค่าที่ไม่เป็นค่าลบของประเภทจำนวนเต็มที่ลงนามเป็นช่วงย่อยของประเภทจำนวนเต็มที่ไม่ได้ลงชื่อที่เกี่ยวข้องและการแทนค่าเดียวกันในแต่ละประเภทจะเหมือนกัน
C89 ไม่ได้บอกอะไรเกี่ยวกับการมีเพียงบิตเครื่องหมายเดียวหรืออนุญาตให้ใช้เฉพาะ twos-complement / ones-complement / sign-magnitude
มาตรฐาน C ++ 98 ใช้ภาษานี้เกือบทุกคำ (มาตรา 3.9.1 ย่อหน้า (3)):
สำหรับประเภทจำนวนเต็มที่ลงนามแต่ละประเภทจะมีประเภทจำนวนเต็มไม่ได้ลงนามที่ตรงกัน (แต่ต่างกัน) : "
unsigned char
", "unsigned short int
", "unsigned int
" และ "unsigned long int
" ซึ่งแต่ละประเภทใช้พื้นที่เก็บข้อมูลเท่ากันและมีข้อกำหนดในการจัดตำแหน่งเดียวกัน (3.9 ) เป็นประเภทจำนวนเต็มลงนามที่เกี่ยวข้อง นั่นคือประเภทจำนวนเต็มที่ลงนามแต่ละประเภทจะมีการแทนอ็อบเจ็กต์เหมือนกับประเภทจำนวนเต็มที่ไม่ได้ลงชื่อ ช่วงของค่าที่ไม่เป็นค่าลบของประเภทจำนวนเต็มที่ลงนามเป็นช่วงย่อยของประเภทจำนวนเต็มไม่ได้ลงนามที่เกี่ยวข้องและการแทนค่าของประเภทที่ลงนาม / ไม่ได้ลงนามแต่ละประเภทจะต้องเหมือนกัน
มาตรฐาน C ++ 03 ใช้ภาษาที่เหมือนกันเป็นหลักเช่นเดียวกับ C ++ 11
ไม่มีข้อมูลจำเพาะ C ++ มาตรฐานที่ จำกัด การแสดงจำนวนเต็มที่ลงนามกับข้อมูลจำเพาะ C ใด ๆ เท่าที่ฉันสามารถบอกได้ และไม่มีอะไรบังคับบิตเซ็นเดียวหรืออะไรก็ได้ สิ่งที่กล่าวมาก็คือจำนวนเต็มที่ลงนามที่ไม่ใช่ค่าลบจะต้องเป็นช่วงย่อยของจำนวนเต็มที่ไม่ได้ลงนาม
ดังนั้นอีกครั้งฉันอ้างว่าอนุญาตให้ใช้ INT_MAX = 32767 กับ INT_MIN = -2 32 +32768 หากคำตอบของคุณถือว่าเป็นอย่างอื่นมันไม่ถูกต้องเว้นแต่คุณจะอ้างถึงมาตรฐานC ++ ที่พิสูจน์ว่าฉันผิด