2021年11月20日
川俣晶の縁側ソフトウェアnew_C#入門・全キーワード明快解説! total 863 count

実数型: 巨大数も極小数ドンと来い!

Written By: 川俣 晶連絡先

この章のテーマ §

 実数型(float/double/decimal型)についての基礎を扱います。

前提知識 §

Console.WriteLineメソッド, 文字列の基礎, メソッドの基礎, (typeof演算子、ジェネリック)

 typeof演算子、ジェネリックについてはサンプルソースで使用しているだけで、特に必須ではない。

解説 §

 C#で小数点以下を扱う場合は、整数型ではなく実数型を使います。

 これらは小数点を左右に移動させることができる浮動小数点という表現方法で数値を表しています。

 C#の実数型はfloat/double/decimalの3つがあります。

 これも、それぞれメモリの消費量と表現できる範囲に差があります。

 (それぞれの型の最大値、最小値はサンプルソースの実行結果参照)

 出力が3.4028235E+38のような見慣れない形式になっていますが、これは指数表示です。特に指定しない場合、極端に大きな数、極端に小さな数は指数表示になります。これは、3.4028235掛ける10の+38乗と読みます。つまり、3.4028235に10を38回掛けた巨大数を意味します。E-38なら10で38回割った数となります。小数点を指定数だけ左右に移動させると考えても構いません。

 ただし最大数、最小数を越えた数は表現できないわけではなく、floatとdouble型はNegativeInfinity(負の無限大)とPositiveInfinity(正の無限大)の値になることができます。

 他にNaN(数値ではない)という状態を取ることもあります。計算結果が数値として表現できない場合は、NaN/NegativeInfinity/PositiveInfinityの状態になることもあるわけです。これは整数型にもdecimal型にもないfloatとdouble型だけの特徴です。

罠の数々 §

  • 浮動小数点演算は予期できない誤差が出やすいので、整数で計算できる場合は使用しない方が望ましいと言える。たとえば、x*0.3と書かずにx*3/10と書く人もいる
  • 極端に大きな数に極端に小さな数を足すと、数が増えないという現象が起きる。有効数字は有限なので、そこから外れた値は結局なかったことになる。後述の【補足・加算が反映されないことがある理由】参照
  • deciaml型はお金の計算で誤差が出にくいように工夫された特別な型なので、誤差を減らしたいときはお勧めである。しかし、メモリ消費量、処理時間は長めになる。速度重視で誤差は度外視するならfloat型がお勧め
  • 内部表現が2進数なので、10進数として割り切れるように見えるシンプルな小数が桁数の多い2進数に変換され、誤差を発生させることがある (整数ならそういうことはない)

補足・加算が反映されないことがある理由 §

 浮動小数点の場合、値を加算しても加算値が反映されないことがある理由を説明します。

 仮にここに0.001から1.000までまで識別できる型があるとします。

 この型で1.001は当然表現できます。しかし、0.0001も表現できます。浮動小数点とは小数点を移動させることで大小に対応する仕掛けなので、小数点を右に4個移動させると0.0001は1(小数点が右に4個移動)として表現できます。

 ところが、1.001+0.0001を計算する場合は、計算結果が1.0011となりますが、最後の1は表現できる範囲を超えるので消えてしまいます。小数点を移動させたらどうかと思うかも知れませんが、もし小数点を左に1つずらすと10.011(小数点が左に1個移動)となって、最も左の1が有効範囲から出てしまって消えてしまいます。

 結局どうやっても桁が足りず、値の一部が消えます。

参考リンク §

浮動小数点数値型 (C# リファレンス)

サンプルソース: realTypes §

sub<float>("float", float.MaxValue, float.MinValue);

sub<double>("double", double.MaxValue, double.MinValue);

sub<decimal>("decimal", decimal.MaxValue, decimal.MinValue);

static void sub<T>(string name, T max, T min) => Console.WriteLine($"名前:{name,-10} 本名:{typeof(T).FullName,-14} 最大値:{max,32} 最小値:{min,32}");

実行結果 §

名前:float      本名:System.Single  最大値:                   3.4028235E+38 最小値:                  -3.4028235E+38

名前:double     本名:System.Double  最大値:         1.7976931348623157E+308 最小値:        -1.7976931348623157E+308

名前:decimal    本名:System.Decimal 最大値:   79228162514264337593543950335 最小値:  -79228162514264337593543950335

リポジトリ §

https://github.com/autumn009/CSharpPrimer2

練習問題 §

 float.MaxValueはfloat型の最大値を取得するプロパティである。

 float.MaxValueの値は3.40282347E+38である。

 では、float.MaxValue+1を計算させると何が起こるだろうか。

  1. 3.40282347E+39
  2. 3.40282348E+38
  3. 3.40282347E+38で変化なし
  4. オーバーフロー例外が起きる
  5. PositiveInfinity(正の無限大)になる

[[解答]]

単行本情報