浮動小数点数の基本構造
浮動小数点数は、コンピュータが実数を近似的に表現するための形式であり、IEEE 754標準に基づいて広く実装されています。C++や.NETにおける float
やdouble
型は、この標準に準拠しています。
IEEE 754 標準に基づく構造(符号部、指数部、仮数部)
IEEE 754 に準拠した浮動小数点数は、以下の3つの部分から構成されます:
Part | 説明 |
---|---|
符号部(Sign) | 数値が正か負かを示す1ビット。0なら正、1なら負。 |
指数部(Exponent) | 数値のスケーリングを示す。バイアス形式で格納される。 |
仮数部(Mantissa / Fraction) | 実際の数値の有効桁部分。暗黙の1を含む場合がある。 |
例:32ビット単精度(float)の構造
| S | Exponent (8 bits) | Fraction (23 bits) |
- 合計 32 ビット
- バイアス値:127
- 精度:約7桁(10進)
例:64ビット倍精度(double)の構造
| S | Exponent (11 bits) | Fraction (52 bits) |
- 合計 64 ビット
- バイアス値:1023
- 精度:約15〜17桁(10進)
仮数の正規化と暗黙の1
IEEE 754 では、仮数部は通常「1.xxx...」の形式で正規化されており、先頭の「1」は格納されず、暗黙的に存在するとみなされます。これにより、1ビット分の精度を節約できます。
バイアス形式の指数
指数部は符号なし整数として格納されますが、実際の指数値は「格納値 − バイアス」で計算されます。たとえば、float 型で指数部が 10000001
(129)の場合、実際の指数は 129 − 127 = 2
です。
特殊値の扱い
IEEE 754 では、以下のような特殊値も定義されています:
+∞
,−∞
(オーバーフロー)NaN
(Not a Number、未定義演算の結果)−0
(符号付きゼロ)
これらは演算結果の判定や例外処理において重要な役割を果たします。
C++ における float
, double
, long double
の違いと用途
浮動小数点数は、実数を近似的に表現するためのデータ型です。C++では主に以下の3種類が用意されています:
float
:単精度浮動小数点数double
:倍精度浮動小数点数long double
:拡張精度浮動小数点数(実装依存)
これらの型は、精度(有効桁数)、メモリ使用量、演算速度に違いがあり、用途に応じて適切に選択する必要があります。
float
- サイズ:32ビット(4バイト)
- 精度:約7桁の有効数字
- 範囲:±3.4×10^38
- 構造:1ビット符号 + 8ビット指数 + 23ビット仮数
- 用途: - 組み込みシステム(メモリ制約が厳しい) - ゲームやグラフィックス(GPUとの互換性)
double
- サイズ:64ビット(8バイト)
- 精度:約15〜16桁の有効数字
- 範囲:±1.7×10^308
- 構造:1ビット符号 + 11ビット指数 + 52ビット仮数
- 用途: - 科学技術計算 - 金融計算 - 一般的な数値処理
long double
- サイズ:実装依存(80, 96, 128ビットなど)
- 精度:最大で約19〜34桁(環境による)
- 構造:x86では80ビット拡張精度(FPU)、x86_64では128ビット(GCC)
- 用途: - 高精度が必要な数値解析 - 物理シミュレーション
long double
のサイズや精度はコンパイラやOSによって異なります。移植性を重視する場合は使用を避けるのが無難です。
.NET における float
, double
, decimal
の違いと用途
.NET(C#)では、実数を扱うために以下の3つの数値型が用意されています:
float
:単精度浮動小数点数(System.Single)double
:倍精度浮動小数点数(System.Double)decimal
:高精度10進数(System.Decimal)
これらの型は、精度・演算速度・用途に違いがあり、適切な選択が重要です。
float
(System.Single)
- サイズ:32ビット(4バイト)
- 精度:約7桁の有効数字
- 範囲:±1.5×10^−45 ~ ±3.4×10^38
- 用途: - グラフィックス処理 - ゲーム開発 - メモリ制約のある環境
double
(System.Double)
- サイズ:64ビット(8バイト)
- 精度:約15〜16桁の有効数字
- 範囲:±5.0×10^−324 ~ ±1.7×10^308
- 用途: - 科学技術計算 - 一般的な数値処理 - デフォルトの浮動小数点型
decimal
(System.Decimal)
- サイズ:128ビット(16バイト)
- 精度:28〜29桁の有効数字(10進数)
- 範囲:±1.0×10^−28 ~ ±7.9×10^28
- 用途: - 金融・会計処理(誤差が許されない) - 通貨計算 - 精密な10進数演算
.NET の decimal
の詳細
float
やdouble
は2進数で数値を表現するため、0.1 や 0.01 のような10進数を正確に扱えず、丸め誤差が発生します。そのため、計算結果に微小な誤差が生じ、これが累積すると実際の金額とズレが生じる可能性があります。一方、decimal
は10進数ベースで数値を扱うため、0.1m + 0.2mのような計算でも誤差なく0.3mという正確な結果が得られます。これは、decimal
が最大28〜29桁の有効数字を持ち、10進数での丸め処理を正確に行えるよう設計されているためです。
たとえば、商品の価格に消費税を加算する場合や、時給と労働時間から給与を計算する場合など、decimal を使うことで「計算結果が1円ずれる」といった問題を防ぐことができます。もちろん、decimal は double よりも演算速度が遅く、メモリ使用量も多いため、科学技術計算や大量の数値処理には向いていません。しかし、**「正確さが最優先される」**場面では、decimal
は不可欠な選択肢です。つまり、decimal
は「誤差が許されない現実世界の金額計算」をプログラムで安全に扱うための、信頼できる数値型と言えます。
🔢 基本的な使い方
decimal price = 199.99m;
decimal taxRate = 0.08m;
decimal tax = price * taxRate;
decimal total = price + tax;
Console.WriteLine($"税額: {tax}");
Console.WriteLine($"合計金額: {total}");
税額: 15.9992
合計金額: 215.9892
m サフィックスは decimal 型を明示するために必要です。199.99 だけでは double として解釈されます。
💰 金融計算の例:通貨の丸め処理
decimal amount = 1234.56789m;
decimal rounded = Math.Round(amount, 2, MidpointRounding.AwayFromZero);
Console.WriteLine($"丸め後: {rounded}"); // 1234.57
- Math.Round を使って 小数第2位で四捨五入。
- MidpointRounding.AwayFromZero は「五捨六入」ではなく「四捨五入」的な挙動。
📊 比較:double vs decimal
double d = 0.1;
decimal m = 0.1m;
Console.WriteLine($"double: 0.1 + 0.2 = {d + 0.2}"); // 0.30000000000000004
Console.WriteLine($"decimal: 0.1 + 0.2 = {m + 0.2m}"); // 0.3
double
は2進数ベースなので、2進数で表現できない数の場合に丸め誤差が出る。decimal
は10進数ベースなので、基数の差異に起因する丸め誤差は生じない。