@Angewが指摘したように、!=
オペレーターは両側に同じ型が必要です。
(float)i != i
フロートへのRHSの促進に結果だけでなく、私たちは持っています(float)i != (float)i
。
g ++も無限ループを生成しますが、内部からの作業を最適化しません。あなたはそれがでINT-> floatを変換見ることができるcvtsi2ss
としucomiss xmm0,xmm0
比較すること(float)i
自体に。(これは、C ++ソースが、@ Angewの答えが説明するように、それが何をしたと思ったのかを意味しないという最初の手がかりでした。)
x != x
x
NaNであったため、「順序付けなし」の場合にのみ当てはまります。(INFINITY
IEEE数学ではそれ自体と同じですが、NaNはそうでNAN == NAN
はありません。false 、NAN != NAN
trueです)。
gcc7.4以前のバージョンでは、コードをjnp
ループブランチ(https://godbolt.org/z/fyOhW1)として正しく最適化しますx != x
。対象のオペランドがNaNでない限り、ループを続けます。(gcc8以降je
では、ループの脱出もチェックされ、NaN以外の入力に対して常にtrueであるという事実に基づいて最適化に失敗します)。x86 FPは、順不同でセットPFを比較します。
そしてところで、その手段打ち鳴らすの最適化にも安全である:それはちょうどCSEにあり(float)i != (implicit conversion to float)i
、同じであるとして、それは証明i -> float
の可能な範囲のためにはNaNになることはありませんint
。
(このループはsigned-overflow UBにヒットすることを前提としていますがud2
、ループ本体が実際に何であるかに関係なく、不正な命令や空の無限ループなど、必要なasmを文字どおりに発行することができます。)ただし、signed-overflow UBは無視します。 、この最適化は依然として100%正当です。
GCCは、ループ本体を離れて最適化に失敗しさえして-fwrapv
明確に定義された符号付き整数オーバーフローするために(2の補数回り込みように)。 https://godbolt.org/z/t9A8t_
有効に-fno-trapping-math
しても役に立ちません。(GCCのデフォルトは、GCCの実装が壊れているかバギーであるにもかかわらず、残念ながら有効になっ
-ftrapping-math
ています。)int-> float変換は、FPの不正確な例外を引き起こす可能性があるため(数値が大きすぎて正確に表現できない場合)、例外がマスクされていない場合、ループ本体を最適化します。(浮動小数点への変換は、不正確な例外がマスクされていない場合、目に見える副作用をもたらす可能性があるためです。)16777217
しかし、-O3 -fwrapv -fno-trapping-math
では、これを空の無限ループにコンパイルしないと、最適化が100%失敗します。なし#pragma STDC FENV_ACCESS ON
では、マスクされたFP例外を記録するスティッキーフラグの状態は、コードの観察可能な副作用ではありません。いいえint
-> float
変換の結果はNaNになるx != x
可能性があるため、trueにすることはできません。
これらのコンパイラーはすべて、IEEE 754単精度(binary32)float
および32ビットを使用するC ++実装用に最適化されていますint
。
バグフィックス(int)(float)i != i
ループは狭い16ビットとC ++の実装上のUBを持つことになりint
、および/またはより広いfloat
あなたがヒットすると思いますので、符号付き整数オーバーフローUBとして正確に表現できなかった最初の整数に到達する前にfloat
。
ただし、x86-64 System V ABIを使用してgccやclangなどの実装用にコンパイルする場合、実装で定義された別の選択肢のセットの下にあるUBは悪影響を及ぼしません。
ところで、このループの結果は、で定義されているFLT_RADIX
とから静的に計算できます。または、少なくとも理論的には、Posit / unumのような他の種類の実数表現ではなく、IEEE floatのモデルに実際に適合する場合は、可能です。FLT_MANT_DIG
<climits>
float
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
、バグ修正されたバージョンは、その前の値ではなく、正確に表現できない最初の整数であるprint になります。
(すべての高い浮動小数点値は正確な整数ですが、有効桁数よりも大きい指数値の場合、2、4、8の倍数などです。多くのより高い整数値を表すことができますが、最後は1単位です。 (仮数の)は1より大きいため、連続した整数ではありません。最大の有限値float
は2 ^ 128未満であり、には大きすぎint64_t
ます。)
コンパイラが元のループを終了して出力した場合は、コンパイラのバグです。