2020年09月14日
川俣晶の縁側ソフトウェアC# コーディング How Tototal 331 count

【入門級】float型double型のカウントアップが不能になる

Written By: 川俣 晶連絡先

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

(経過時間に関しては実行する環境、状態によって変化する)

Facebook

COOL C# CREW

キーワード【 川俣晶の縁側ソフトウェアC# コーディング How To
【C# コーディング How To】の次のコンテンツ
2020年
09月
16日
【入門級】byte?とshortはどちらがメモリ効率が良いか?
3days 51 count
total 307 count
【C# コーディング How To】の前のコンテンツ
2020年
09月
13日
【入門級】簡単なfor文がハングアップするとき
3days 3 count
total 323 count

このコンテンツを書いた川俣 晶へメッセージを送る

[メッセージ送信フォームを利用する]

メッセージ送信フォームを利用することで、川俣 晶に対してメッセージを送ることができます。

この機能は、100%確実に川俣 晶へメッセージを伝達するものではなく、また、確実に川俣 晶よりの返事を得られるものではないことにご注意ください。

このコンテンツへトラックバックするためのURL

http://mag.autumn.org/tb.aspx/20200914095121
サイトの表紙【C# コーディング How To】の表紙【C# コーディング How To】のコンテンツ全リスト 【C# コーディング How To】の入手全リスト 【C# コーディング How To】のRSS1.0形式の情報このサイトの全キーワードリスト 印刷用ページ

管理者: 川俣 晶連絡先

Powered by MagSite2 Version 0.34 (Alpha-Test) Copyright (c) 2004-2018 Pie Dey.Co.,Ltd.