@Angew'in belirttiği gibi , !=
operatörün her iki tarafta da aynı türe ihtiyacı var.
(float)i != i
RHS'nin de dalgalanması ile sonuçlanır, bu yüzden var (float)i != (float)i
.
g ++ ayrıca sonsuz bir döngü oluşturur, ancak içindeki işi optimize etmez. Bunu birlikte int-> şamandıra dönüştürür görebilirsiniz cvtsi2ss
ve yapar ucomiss xmm0,xmm0
karşılaştırmak için (float)i
kendisiyle. (Bu, C ++ kaynağınızın sandığınız şeyi ifade etmediğine dair ilk ipucunuzdu @ Angew'in cevabı açıklıyor.)
x != x
x
NaN olduğundan yalnızca "sırasız" olduğunda doğrudur . ( INFINITY
IEEE matematiğinde kendisine eşittir, ancak NaN değildir. NAN == NAN
yanlıştır, NAN != NAN
doğrudur).
gcc7.4 ve daha eski sürüm, kodunuzu jnp
döngü dalı olarak doğru şekilde optimize eder ( https://godbolt.org/z/fyOhW1 ): işlenenler x != x
NaN olmadığı sürece döngüye devam . (gcc8 ve sonraki sürümler ayrıca je
döngüde bir kesinti olup olmadığını kontrol eder ve NaN olmayan herhangi bir girdi için her zaman doğru olacağı gerçeğine dayanarak optimizasyon yapamaz). x86 FP, set PF'yi sırasız olarak karşılaştırır.
Ve BTW, bu clang'ın optimizasyonunun da güvenli olduğu anlamına gelir : sadece CSE'ye ihtiyaç duyar(float)i != (implicit conversion to float)i
aynı olduğu ve i -> float
bunun olası aralık için asla NaN olmadığını kanıtlaması gerekir int
.
(Bu döngünün işaretli taşma UB'ye çarpacağı göz önüne alındığında, ud2
yasadışı bir talimat veya döngü gövdesinin gerçekte ne olduğuna bakılmaksızın boş bir sonsuz döngü dahil olmak üzere, kelimenin tam anlamıyla istediği herhangi bir asm yayınlamasına izin verilir .) , bu optimizasyon hala% 100 yasaldır.
GCC , işaretli tamsayı taşmasını iyi tanımlanmış yapmak için bile-fwrapv
döngü gövdesini optimize edemiyor (2'nin tamamlayıcı sarmalaması olarak). https://godbolt.org/z/t9A8t_
Etkinleştirmek bile -fno-trapping-math
yardımcı olmuyor. (GCC'nin varsayılanı, maalesef GCC'nin uygulaması bozuk / hatalı
-ftrapping-math
olsa bile etkinleştirmektir .) İnt-> float dönüşümü, kesin olmayan bir FP istisnasına neden olabilir (tam olarak temsil edilemeyecek kadar büyük sayılar için), bu nedenle, muhtemelen maskelenmemiş istisnalar dışında, makul değildir. döngü gövdesini optimize edin. (Çünkü gerçek olmayan istisna maskelenmemişse, float türüne dönüştürmek gözlemlenebilir bir yan etkiye sahip olabilir.)16777217
Ancak -O3 -fwrapv -fno-trapping-math
, bunu boş bir sonsuz döngüye derlememek% 100 eksik optimizasyondur. Aksi #pragma STDC FENV_ACCESS ON
takdirde, maskelenmiş FP istisnalarını kaydeden yapışkan bayrakların durumu, kodun gözlemlenebilir bir yan etkisi değildir. Hayır int
-> float
dönüştürme NaN ile sonuçlanabilir, bu nedenlex != x
doğru olamaz.
Bu derleyicilerin tümü, IEEE 754 tek duyarlıklı (binary32) float
ve 32-bit kullanan C ++ uygulamaları için optimize ediyorint
.
Hata düzeltilmiş(int)(float)i != i
döngü, dar 16 bit int
ve / veya daha geniş olan C ++ uygulamalarında UB'ye sahip olacaktır float
, çünkü tam olarak gösterilemeyen ilk tamsayıya ulaşmadan önce işaretli tamsayı taşması UB'ye ulaşırsınızfloat
.
Ancak, farklı bir uygulama tanımlı seçimler kümesi altındaki UB, x86-64 System V ABI ile gcc veya clang gibi bir uygulama için derlerken herhangi bir olumsuz sonuç doğurmaz.
BTW, statik olarak bu döngünün sonucu hesaplamak olabilir FLT_RADIX
ve FLT_MANT_DIG
tanımlanan <climits>
. Ya da en azından teoride yapabilirsin, eğerfloat
bir Pozit / unum gibi başka bir tür gerçek sayı temsilinden ziyade bir IEEE kayan modeline .
ISO C ++ standardının float
davranış hakkında ne kadar bilgi verdiğinden ve sabit genişlikli üs ve anlamlı alanlara dayalı olmayan bir formatın standartlara uygun olup olmayacağından emin değilim .
Yorumlarda:
@geza Ortaya çıkan sayıyı duymak isterim!
@nada: 16777216
Yazdırmak / geri dönmek için bu döngüye sahip olduğunuzu mu iddia ediyorsunuz 16777216
?
Güncelleme: bu yorum silindiğinden, sanmıyorum. Muhtemelen OP, float
tam olarak 32 bit olarak temsil edilemeyen ilk tam sayıdan önceki alıntıdır float
. https://en.wikipedia.org/wiki/Single-precision_floating-point_format#Precision_limits_on_integer_values yani bu buggy koduyla neyi doğrulamayı umuyorlar.
Hata düzeltilmiş sürüm elbette yazdırılır 16777217
, ilk tam sayı değildir doğrusu bundan önce değerinden daha tam olarak gösterilemeyecek.
(Tüm yüksek kayan değerler tam sayılardır, ancak anlamlı ve genişliğinden daha yüksek üslü değerler için 2'nin, sonra 4'ün, ardından 8'in vb. Katlarıdır. Daha yüksek birçok tam sayı değeri gösterilebilir, ancak son sırada 1 birim (anlamlı) 1'den büyük olduğundan bitişik tamsayı değillerdir. En büyük sonlu float
2 ^ 128'in hemen altındadır ve bu bile çift için çok büyüktürint64_t
.)
Herhangi bir derleyici orijinal döngüden çıkıp bunu yazdırırsa, bu bir derleyici hatası olur.