Seperti yang ditunjukkan @Angew , !=
operator membutuhkan tipe yang sama di kedua sisi.
(float)i != i
Hasil 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 cvtsi2ss
dan tidak ucomiss xmm0,xmm0
membandingkan (float)i
dengan dirinya sendiri. (Itu adalah petunjuk pertama Anda bahwa sumber C ++ Anda tidak berarti apa yang Anda pikir seperti yang dijelaskan oleh jawaban @ Angew.)
x != x
hanya benar jika "tidak diurutkan" karena x
adalah NaN. ( INFINITY
membandingkan sama dengan dirinya dalam matematika IEEE, tetapi NaN tidak. NAN == NAN
salah, NAN != NAN
benar).
gcc7.4 dan yang lebih lama mengoptimalkan kode Anda dengan benar jnp
sebagai cabang perulangan ( https://godbolt.org/z/fyOhW1 ): terus lakukan perulangan selama operan x != x
tidak NaN. (gcc8 dan yang lebih baru juga memeriksa je
keluarnya 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)i
yang harus sama, dan membuktikan bahwa i -> float
tidak 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 ud2
instruksi 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 -fwrapv
untuk membuat luapan bilangan bulat yang ditandatangani terdefinisi dengan baik (sebagai sampul pelengkap 2). https://godbolt.org/z/t9A8t_
Bahkan mengaktifkan -fno-trapping-math
tidak membantu. ( Sayangnya , default GCC diaktifkan
-ftrapping-math
meskipun 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 16777217
ke 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
-> float
konversi dapat menghasilkan NaN, jadi x != x
tidak mungkin benar.
Semua compiler ini mengoptimalkan implementasi C ++ yang menggunakan presisi tunggal IEEE 754 (binary32) float
dan 32-bit int
.
The bugfixed(int)(float)i != i
lingkaran akan memiliki UB pada C ++ implementasi dengan sempit 16-bit int
dan / 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_RADIX
dan FLT_MANT_DIG
, didefinisikan dalam <climits>
. Atau setidaknya Anda bisa secara teori, jika float
benar-benar sesuai dengan model float IEEE daripada beberapa jenis representasi bilangan nyata seperti Posit / unum.
Saya tidak yakin seberapa besar standar ISO C ++ tentang float
perilaku 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 float
sebelum 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 float
adalah 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.