C# float 無限ループ
-
19-09-2019 - |
質問
C# (.Net 3.5 SP1) の次のコードは、私のマシンでは無限ループです。
for (float i = 0; i < float.MaxValue; i++) ;
数値 16777216.0 に達し、16777216.0 + 1 は 16777216.0 と評価されます。まだこの時点では:i + 1 != i。
これはちょっとした狂気だ。
浮動小数点数の格納方法に多少の不正確さがあることに気づきました。そして、2^24 より大きい整数は float として適切に格納できないことを読みました。
上記のコードは、数値が適切に表現できない場合でも、C# では有効なはずです。
なぜ機能しないのでしょうか?
二重でも同じことが起こりますが、非常に長い時間がかかります。9007199254740992.0 が double の制限です。
解決
そうです、問題は、float に 1 を追加するには、次のようにする必要があるということです。
16777217.0
これはたまたま基数の境界にあるため、浮動小数点として正確に表すことができません。(利用可能な次に高い値は 16777218.0
)
したがって、最も近い表現可能な浮動小数点数に丸められます。
16777216.0
このように言ってみましょう:
あなたが持っているので、 フローティング 精度の量を増やすには、数値をどんどん大きくする必要があります。
編集:
OK、これを説明するのは少し難しいですが、次のことを試してください。
float f = float.MaxValue;
f -= 1.0f;
Debug.Assert(f == float.MaxValue);
この値では、1.0f の差を表すには 128 ビットを超える精度が必要になるため、これは問題なく実行されます。float には 32 ビットしかありません。
編集2
私の計算によると、少なくとも 128 の 2 進数 署名されていない 必要だろう。
log(3.40282347E+38) * log(10) / log(2) = 128
問題の解決策として、2 つの 128 ビット数値をループすることができます。ただし、これが完了するには少なくとも10年はかかります。
他のヒント
浮動小数点数は最大2大きな桁で表現、プラスの指数であることを例えば想像してみてください。次は100になりますが、あなたは唯一の「2のべき乗に1.0倍の10」として格納される2桁を持つことができますので。その1つを追加するだろう...何?
せいぜい、それは実際に再び「2のべき乗の1.0〜10倍」として(無意味3桁目を破棄丸め誤差を介して)格納される中間結果として101であろう。
あなたは浮動小数点のIEEE標準を読んでする必要があるとしている間違っている何が起こっているかを理解するには>
第二のためののは、浮動小数点の数:
浮動小数点数は、2つの部分に分割(OK 3が、2番目の符号ビットは無視される)。
あなたは、指数と仮数を持っています。これと同様ます:
smmmmmmmmeeeeeee
注:それはビット数にacurateではありませんが、それはあなたに何が起こっているかの一般的なアイデアを提供します。
あなたは、我々は次の計算を行う必要があり数えるかを把握するために、
mmmmmm * 2^(eeeeee) * (-1)^s
だから何float.MaxValueはなるだろうか?さてあなたは、可能な限り最大の仮数と最大の可能な指数を持っているつもりです。さんがこのようになりますふりしてみましょう。
01111111111111111
実際には、我々はNANと+ -INFやカップル他の規則を定義しますが、彼らはあなたの質問に関連していないだから秒のためにそれらを無視します。
あなたは9.9999*2^99 + 1
を持っているとき、だから、何が起こるでしょうか?さて、あなたはそれが同じ数に切り捨てられます、結果として1を追加するのに十分な有効数字を持っていません。単一の浮動小数点精度の場合に+1
が切り捨て取得し始める点が16777216.0
deするたまたま
これは、オーバーフロー、または最大値に近いものとは何の関係もありません。 16777216.0のためのfloat値は、それが16777217.0のバイナリ表現が16777216であることを除いて、16777217.0でなければなりませんので、あなたは、その後、1でそれをインクリメント16777216のバイナリ表現を持っています!だから、実際にはインクリメントされないか、少なくとも増分は何を期待しません。
ここでは、これを示しジョンスキートによって書かれたクラスがあります:
これでこのコードを試してみてください。
double d1 = 16777217.0;
Console.WriteLine(DoubleConverter.ToExactString(d1));
float f1 = 16777216.0f;
Console.WriteLine(DoubleConverter.ToExactString(f1));
float f2 = 16777217.0f;
Console.WriteLine(DoubleConverter.ToExactString(f2));
16777216.0の内部表現がどのように注意してください同じ16777217.0 !!
私はfloat.MaxValueはちょうどこの値より私を持って近づい反復。次の反復は、私に追加されますが、それはfloat.MaxValueよりも大きな数を保持することはできません。このように、それははるかに小さい値を保持し、再びループを開始します。