MaxValueが期待を裏切るとき §
MaxValueフィールドはその型で扱える数値の上限を教えてくれるはずです。
変数を1つ1つカウントアップして行くと、MaxValueまではカウントアップできるはずです。
ところが、float型double型ではこの期待が裏切られます。
値がMaxValueに達していないのに、カウントアップされない状態に陥ってしまいます。
サンプルソースはその状態を検出しています。
浮動小数点の正しい意味 §
float型double型decimal型は浮動小数点です。浮動小数点とは、小数点の位置を前後にずらして極端に大きな数、極端に小さな数を扱えるようにする技術です。
たとえば、100という数字の桁数をずらして、百万という数字を表現します。
つまり、[1][0][0]を[1][0][0](0)(0)(0)(0)と見なします。この時、(0)は実際にはないがあると見なした桁です。ですから計算できません。この価に1を足しても実際には下一桁は増えません。
どうすれば解決できる? §
浮動小数点のfloat型double型でのMaxValueフィールドの正しい意味は【表現できる最大の数】であって、【カウントアップ可能な最大の数】ではありません。
1つ1つ確実にカウントアップしたいなら、浮動小数点ではないint型やlong型を使うのがお勧めです。
罠の数々 §
- 浮動小数点の場合、下の方の桁には無視される桁が発生する場合がある。小数点の位置に関係なく桁数の幅が広いときは起こりうる。たとえば、0.000000001なら表現できても、1.000000001の最後の桁は表現できない可能性がある。
- そうでなくても、浮動小数点は丸め誤差が発生しやすい。整数の計算に持ち込めるのなら整数の計算に持ち込みたい。たとえば、ある数の10%を計算するなら、"*0.1"ではなく、"/10"を使いたい。
- float型double型のPositiveInfinityフィールドは無限大を意味する。これはMaxValueフィールドとは違うことに注意しよう。MaxValueフィールドは数値として扱える上限である。PositiveInfinityフィールドは無限大という状態である。
- マイナスの無限大を表すNegativeInfinityもある。
- 実際にはdecimal型も浮動小数点型の一種である。しかし、decimal型にPositiveInfinity/NegativeInfinityはない。decimal型は微妙に立場が違う
参考リンク §
浮動小数点数値型 (C# リファレンス)
Single.MaxValue フィールド
Double.MaxValue フィールド
Double.PositiveInfinity フィールド
APIリファレンスでは、double型に対応するのはDouble構造体で分かりやすいが、float型に対応するのはSingle構造体であって名前が大幅に違う。上記リンクから飛んで確認しておこう。
また、float型がカウントアップ不能になるまであっという間だが、double型は遙かに時間が掛かる。なぜその違いが起きるのかを上記リンクの情報から考えてみよう。(下記サンプルソースではfloat型は1ずつ増やしているがdouble型は時間が掛かりすぎるので2倍ずつ増やしている)
リポジトリ §
https://github.com/autumn009/cshowto
DoubleMax §
using System;
class Program
{
static void Main()
{
float lastOne = 0.0f;
DateTimeOffset start = DateTimeOffset.Now;
for (float i = 1.0f; i < float.MaxValue;)
{
if (i > 16777200) Console.WriteLine(i);
if (lastOne == i)
{
Console.WriteLine($"カウントアップしていません! {i} {DateTimeOffset.Now - start}");
break;
}
lastOne = i;
i++;
}
DateTimeOffset startD = DateTimeOffset.Now;
for (double i = 1.0; i < double.MaxValue;i*=2)
{
double next = i + 1;
if (i == next)
{
Console.WriteLine($"カウントアップしていません! {i} {DateTimeOffset.Now - startD}");
return;
}
}
}
}
実行結果
16777201
16777202
16777203
16777204
16777205
16777206
16777207
16777208
16777209
16777210
16777211
16777212
16777213
16777214
16777215
16777216
16777216
カウントアップしていません! 16777216 00:00:00.1160752
カウントアップしていません! 9007199254740992 00:00:00.0000017
(経過時間に関しては実行する環境、状態によって変化する)