Seperti yang ditunjukkan @Angew , !=operator membutuhkan tipe yang sama di kedua sisi.
(float)i != iHasil promosi RHS juga mengambang, jadi kami punya (float)i != (float)i.
g ++ juga menghasilkan loop tak terbatas, tetapi tidak mengoptimalkan pekerjaan dari dalamnya. Anda dapat melihatnya mengubah int-> float dengan cvtsi2ssdan tidak ucomiss xmm0,xmm0membandingkan (float)idengan dirinya sendiri. (Itu adalah petunjuk pertama Anda bahwa sumber C ++ Anda tidak berarti apa yang Anda pikir seperti yang dijelaskan oleh jawaban @ Angew.)
x != xhanya benar jika "tidak diurutkan" karena xadalah NaN. ( INFINITYmembandingkan sama dengan dirinya dalam matematika IEEE, tetapi NaN tidak. NAN == NANsalah, NAN != NANbenar).
gcc7.4 dan yang lebih lama mengoptimalkan kode Anda dengan benar jnpsebagai cabang perulangan ( https://godbolt.org/z/fyOhW1 ): terus lakukan perulangan selama operan x != x tidak NaN. (gcc8 dan yang lebih baru juga memeriksa jekeluarnya loop, gagal mengoptimalkan berdasarkan fakta bahwa itu akan selalu benar untuk input non-NaN apa pun). x86 FP membandingkan set PF pada unordered.
Dan BTW, itu berarti pengoptimalan clang juga aman : hanya CSE (float)i != (implicit conversion to float)iyang harus sama, dan membuktikan bahwa i -> floattidak pernah NaN untuk rentang yang memungkinkan int.
(Meskipun mengingat bahwa loop ini akan mengenai UB yang ditandatangani-overflow, diizinkan untuk memancarkan secara harfiah asm apa pun yang diinginkannya, termasuk ud2instruksi ilegal, atau loop tanpa batas kosong terlepas dari apa badan loop sebenarnya.) Tetapi mengabaikan UB yang ditandatangani-overflow , pengoptimalan ini masih 100% legal.
GCC gagal untuk mengoptimalkan badan pengulangan bahkan dengan -fwrapvuntuk membuat luapan bilangan bulat yang ditandatangani terdefinisi dengan baik (sebagai sampul pelengkap 2). https://godbolt.org/z/t9A8t_
Bahkan mengaktifkan -fno-trapping-mathtidak membantu. ( Sayangnya , default GCC diaktifkan
-ftrapping-mathmeskipun penerapan GCC-nya rusak / buggy .) Konversi int-> float dapat menyebabkan pengecualian FP yang tidak tepat (untuk angka yang terlalu besar untuk diwakili dengan tepat), jadi dengan pengecualian yang mungkin dibuka kedoknya, masuk akal untuk tidak mengoptimalkan tubuh lingkaran. (Karena mengonversi 16777217ke float bisa memiliki efek samping yang dapat diamati jika pengecualian tidak tepat dibuka kedoknya.)
Tapi dengan -O3 -fwrapv -fno-trapping-math, itu 100% melewatkan pengoptimalan untuk tidak mengkompilasi ini ke loop tanpa batas kosong. Tanpa #pragma STDC FENV_ACCESS ON, status sticky flag yang merekam pengecualian FP terselubung bukanlah efek samping kode yang dapat diamati. Tidak int-> floatkonversi dapat menghasilkan NaN, jadi x != xtidak mungkin benar.
Semua compiler ini mengoptimalkan implementasi C ++ yang menggunakan presisi tunggal IEEE 754 (binary32) floatdan 32-bit int.
The bugfixed(int)(float)i != i lingkaran akan memiliki UB pada C ++ implementasi dengan sempit 16-bit intdan / atau lebih luas float, karena Anda akan memukul menandatangani-bulat meluap UB sebelum mencapai bilangan bulat pertama yang tidak persis representable sebagai float.
Tetapi UB di bawah kumpulan pilihan yang ditentukan implementasi yang berbeda tidak memiliki konsekuensi negatif saat mengompilasi untuk implementasi seperti gcc atau clang dengan ABI Sistem V x86-64.
BTW, Anda dapat menghitung hasil loop ini secara statis dari FLT_RADIXdan FLT_MANT_DIG, didefinisikan dalam <climits>. Atau setidaknya Anda bisa secara teori, jika floatbenar-benar sesuai dengan model float IEEE daripada beberapa jenis representasi bilangan nyata seperti Posit / unum.
Saya tidak yakin seberapa besar standar ISO C ++ tentang floatperilaku dan apakah format yang tidak didasarkan pada bidang eksponen dan signifikansi lebar tetap akan memenuhi standar.
Dalam komentar:
@geza Saya akan tertarik untuk mendengar nomor yang dihasilkan!
@nada: ini 16777216
Apakah Anda mengklaim memiliki simpul ini untuk dicetak / dikembalikan 16777216?
Pembaruan: karena komentar itu telah dihapus, saya kira tidak. Mungkin OP hanya mengutip floatsebelum bilangan bulat pertama yang tidak dapat secara tepat direpresentasikan sebagai 32-bit float. https://en.wikipedia.org/wiki/Single-precision_floating-point_format#Precision_limits_on_integer_values yaitu apa yang ingin mereka verifikasi dengan kode buggy ini.
Versi perbaikan bug tentu saja akan dicetak 16777217, bilangan bulat pertama yang tidak dapat direpresentasikan, bukan nilai sebelumnya.
(Semua nilai float yang lebih tinggi adalah bilangan bulat yang tepat, tetapi merupakan kelipatan 2, lalu 4, lalu 8, dll. Untuk nilai eksponen yang lebih tinggi dari lebar signifikan. Banyak nilai bilangan bulat yang lebih tinggi dapat diwakili, tetapi 1 unit di tempat terakhir (dari signifikan) lebih besar dari 1 jadi mereka bukan bilangan bulat yang berdekatan. Terbatas terbesar floatadalah tepat di bawah 2 ^ 128, yang terlalu besar untuk genap int64_t.)
Jika ada kompilator yang keluar dari loop asli dan mencetaknya, itu akan menjadi bug kompilator.