ดังที่ @Angew ชี้ให้เห็นตัว!=ดำเนินการต้องการแบบเดียวกันทั้งสองด้าน
(float)i != iผลลัพธ์ในโปรโมชั่นของ RHS (float)i != (float)iจะลอยเป็นอย่างดีเพื่อให้เรามี
g ++ ยังสร้างลูปที่ไม่มีที่สิ้นสุด แต่ไม่ได้เพิ่มประสิทธิภาพการทำงานจากภายใน คุณสามารถเห็นมันแปลง int-> ลอยด้วยcvtsi2ssและucomiss xmm0,xmm0เปรียบเทียบ(float)iกับตัวมันเอง (นั่นเป็นเบาะแสแรกของคุณที่แหล่งที่มา C ++ ของคุณไม่ได้หมายความว่าสิ่งที่คุณคิดไว้เหมือนที่คำตอบของ @ Angew อธิบาย)
x != xจะเป็นจริงก็ต่อเมื่อ "ไม่เรียงลำดับ" เพราะxเป็น NaN ( INFINITYเปรียบเทียบกับตัวมันเองในคณิตศาสตร์ IEEE แต่ NaN ไม่ NAN == NANเป็นเท็จNAN != NANเป็นจริง)
gcc7.4 และเก่ากว่าปรับแต่งโค้ดของคุณให้เหมาะสมjnpเป็นสาขาลูป ( https://godbolt.org/z/fyOhW1 ): วนลูปต่อไปตราบเท่าที่ตัวถูกดำเนินการx != x ไม่ใช่ NaN (gcc8 และในภายหลังยังตรวจสอบjeเพื่อแยกออกจากลูปไม่สามารถปรับให้เหมาะสมได้โดยอิงจากข้อเท็จจริงที่ว่ามันจะเป็นจริงเสมอสำหรับอินพุตที่ไม่ใช่ NaN) x86 FP เปรียบเทียบชุด PF กับ unordered
และ BTW นั่นหมายความว่าการเพิ่มประสิทธิภาพของ clang นั้นปลอดภัยเช่นกันโดยจะต้องมี CSE (float)i != (implicit conversion to float)iเหมือนกันและพิสูจน์ได้ว่าi -> floatไม่มี NaN สำหรับช่วงที่เป็นไปได้ของint.
(แม้ว่าลูปนี้จะเข้าสู่ UB ที่มีการลงชื่อมากเกินไป แต่ก็อนุญาตให้ปล่อย asm ที่ต้องการได้อย่างแท้จริงรวมถึงud2คำสั่งที่ผิดกฎหมายหรือการวนซ้ำที่ว่างเปล่าโดยไม่คำนึงว่าเนื้อหาของลูปเป็นอย่างไร) แต่การเพิกเฉยต่อ UB ที่ลงนามมากเกินไป การเพิ่มประสิทธิภาพนี้ยังคงถูกกฎหมาย 100%
GCC ล้มเหลวในการเพิ่มประสิทธิภาพของร่างกายออกไปห่วงแม้จะมีการ-fwrapvที่จะทำให้การลงนามจำนวนเต็มล้นที่ดีที่กำหนด (เป็น 2 ส่วนเติมเต็มวิจิตร) https://godbolt.org/z/t9A8t_
แม้แต่การเปิดใช้งาน-fno-trapping-mathก็ไม่ช่วย (ค่าเริ่มต้นของ GCC น่าเสียดายที่จะเปิดใช้งาน
-ftrapping-mathแม้ว่าการใช้งาน GCC จะเสีย / มีข้อผิดพลาด) การแปลง int-> float อาจทำให้เกิดข้อยกเว้นที่ไม่แน่นอนของ FP (สำหรับตัวเลขที่ใหญ่เกินไปที่จะแสดงอย่างแน่นอน) ดังนั้นด้วยข้อยกเว้นที่อาจเปิดเผยได้จึงมีเหตุผลที่จะไม่ เพิ่มประสิทธิภาพร่างกายห่วง (เนื่องจากการแปลง16777217เป็น float อาจมีผลข้างเคียงที่สังเกตเห็นได้หากไม่มีการปิดบังข้อยกเว้นที่ไม่แน่นอน)
แต่ด้วย-O3 -fwrapv -fno-trapping-mathการเพิ่มประสิทธิภาพที่พลาดไป 100% ที่จะไม่รวบรวมสิ่งนี้เป็นลูปที่ไม่มีที่สิ้นสุด หากไม่มี#pragma STDC FENV_ACCESS ONสถานะของแฟล็กติดหนึบที่บันทึกข้อยกเว้นของ FP ที่ถูกปิดบังจะไม่ใช่ผลข้างเคียงที่สังเกตได้ของโค้ด ไม่int-> floatการแปลงอาจทำให้เกิด NaN ดังนั้นจึงx != xไม่สามารถเป็นจริงได้
คอมไพเลอร์เหล่านี้มีการเพิ่มประสิทธิภาพทั้งหมดสำหรับ C ++ การใช้งานที่ใช้ IEEE 754 ความแม่นยำเดียว (binary32) floatและ int32
การแก้ปัญหา(int)(float)i != iห่วงจะมี UB ใน c ++ การใช้งานกับแคบ 16 บิตintและ / หรือที่กว้างขึ้นfloatเพราะคุณจะตีลงนามจำนวนเต็มล้น UB floatก่อนที่จะถึงจำนวนเต็มแรกที่ไม่ตรงซึ่งแสดงเป็น
แต่ UB ภายใต้ชุดตัวเลือกที่กำหนดการนำไปใช้งานที่แตกต่างกันจะไม่มีผลเสียใด ๆ เมื่อคอมไพล์สำหรับการนำไปใช้งานเช่น gcc หรือ clang ด้วย x86-64 System V ABI
BTW คุณสามารถคำนวณผลลัพธ์ของลูปนี้แบบคงที่จากFLT_RADIXและที่FLT_MANT_DIGกำหนดใน<climits>. หรืออย่างน้อยคุณก็สามารถในทางทฤษฎีถ้าfloatจริง ๆ แล้วเหมาะกับแบบจำลองของการลอยตัวของ IEEE มากกว่าการแทนจำนวนจริงประเภทอื่น ๆ เช่น Posit / unum
ฉันไม่แน่ใจว่ามาตรฐาน ISO C ++ เกี่ยวกับfloatพฤติกรรมมากน้อยเพียงใดและรูปแบบที่ไม่ได้ขึ้นอยู่กับฟิลด์เลขชี้กำลังและนัยสำคัญคงที่จะเป็นไปตามมาตรฐานหรือไม่
ในความคิดเห็น:
@geza ฉันสนใจที่จะได้ยินจำนวนผลลัพธ์!
@nada: มัน 16777216
คุณอ้างว่าคุณมีลูปนี้เพื่อพิมพ์ / ส่งคืน16777216หรือไม่?
อัปเดต: เนื่องจากความคิดเห็นนั้นถูกลบฉันคิดว่าไม่ อาจเป็นไปได้ว่า OP เป็นเพียงการอ้างถึงfloatจำนวนเต็มแรกที่ไม่สามารถแสดงเป็น 32 บิตfloatได้ทั้งหมด https://en.wikipedia.org/wiki/Single-precision_floating-point_format#Precision_limits_on_integer_values นั่นคือสิ่งที่พวกเขาหวังจะตรวจสอบด้วยรหัสบั๊กกี้นี้
แน่นอนว่าเวอร์ชันที่แก้ไขข้อบกพร่องจะพิมพ์ออกมา16777217ซึ่งเป็นจำนวนเต็มแรกที่ไม่สามารถแสดงได้อย่างแน่นอนแทนที่จะเป็นค่าก่อนหน้านั้น
(ค่าลอยตัวที่สูงกว่าทั้งหมดเป็นจำนวนเต็มแน่นอน แต่เป็นจำนวนทวีคูณของ 2 จากนั้น 4 แล้ว 8 เป็นต้นสำหรับค่าเลขชี้กำลังที่สูงกว่าความกว้างนัยสำคัญสามารถแสดงค่าจำนวนเต็มที่สูงกว่าจำนวนมากได้ แต่จะมี 1 หน่วยในตำแหน่งสุดท้าย (ของนัยสำคัญ) มีค่ามากกว่า 1 ดังนั้นจึงไม่เป็นจำนวนเต็มติดกันจำนวน จำกัด ที่ใหญ่ที่สุดfloatคือต่ำกว่า 2 ^ 128 ซึ่งใหญ่เกินไปสำหรับคู่int64_t)
หากคอมไพเลอร์ตัวใดออกจากลูปเดิมและพิมพ์สิ่งนั้นจะเป็นบั๊กของคอมไพเลอร์