ดังที่ @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
และ int
32
การแก้ปัญหา(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
)
หากคอมไพเลอร์ตัวใดออกจากลูปเดิมและพิมพ์สิ่งนั้นจะเป็นบั๊กของคอมไพเลอร์