C#は、厳格な型システムを持つプログラミング言語であり、数値を扱うための型が豊富に用意されています。

アプリケーションの開発において、適切な数値型を選択することは、メモリ効率の向上、計算精度の維持、そして予期せぬバグの回避に直結します。

特に、メモリリソースが限られた環境や、極めて高い精度が求められる金融システムの開発では、それぞれの型の特性を深く理解しておくことが不可欠です。

本記事では、C#で利用可能な数値型の種類、範囲、サイズから、実務における具体的な使い分けまで、プロフェッショナルの視点で詳しく解説します。

C#における数値型の基本概念

C#の数値型は、すべて「値型(Value Type)」に分類されます。

これは、変数がデータそのものを直接保持することを意味し、スタックメモリ上で効率的に管理されます。

また、C#の数値型は.NETの共通型システム(CTS)に基づいているため、C#独自のキーワード(例:int)と.NETの構造体名(例:System.Int32)が対応しています。

数値型は大きく分けて以下の4つのカテゴリーに分類できます。

  1. 整数型:小数を扱わない数値(正、負、ゼロ)。
  2. 浮動小数点型:科学計算などに適した、極めて広い範囲を扱える型。
  3. 10進数型(decimal):金融計算に適した、誤差の極めて少ない型。
  4. ネイティブサイズ整数:プラットフォームのビット幅に依存する型。

これらの型を適切に選択するためには、まずそれぞれのスペックを正確に把握する必要があります。

整数型(Integer Types)の詳解

整数型は、ビット数や符号(正負)の有無によって細かく分かれています。

現代のアプリケーション開発において最も頻繁に使用されるのは int ですが、状況に応じて他の型を検討する必要があります。

整数型のサイズと値の範囲一覧

以下の表は、C#で定義されている標準的な整数型の一覧です。

型キーワード.NET構造体名サイズ (bit)範囲
sbyteSystem.SByte8-128 ~ 127
byteSystem.Byte80 ~ 255
shortSystem.Int1616-32,768 ~ 32,767
ushortSystem.UInt16160 ~ 65,535
intSystem.Int3232約 -21億 ~ 21億
uintSystem.UInt32320 ~ 約 42億
longSystem.Int6464約 -922京 ~ 922京
ulongSystem.UInt64640 ~ 約 1,844京

整数型の使い分けガイドライン

日常的なプログラミングにおいては、原則として int を使用することが推奨されます。

これは、現代の32ビットおよび64ビットCPUにおいて、32ビット整数の演算が最も最適化されているためです。

  • int (System.Int32): ループのカウンタ、配列のインデックス、一般的な数量などに使用します。
  • long (System.Int64): int の範囲を超える可能性がある場合(例:SNSの投稿ID、ファイルサイズ、世界人口など)に使用します。
  • byte (System.Byte): 画像データ、音声データ、ネットワーク通信のパケットなど、バイナリデータを扱う場合に使用します。
  • short / sbyte: 大量の数値をメモリ上に保持する必要があり、メモリ使用量を極限まで抑えたい特殊なケース(例:大規模な数値配列、組み込みデバイス)に限定して使用します。

整数型の使用例

C#
using System;

class Program
{
    static void Main()
    {
        // 一般的な整数の宣言
        int userAge = 25;
        
        // 大きな数値(long型には L サフィックスを付けるのが一般的)
        long distanceToStar = 9460730472580L;
        
        // 符号なし整数(正の数のみ)
        uint positiveOnly = 4000000000U;

        Console.WriteLine($"Age: {userAge}");
        Console.WriteLine($"Distance: {distanceToStar}");
        Console.WriteLine($"Unsigned: {positiveOnly}");
    }
}
実行結果
Age: 25
Distance: 9460730472580
Unsigned: 4000000000

浮動小数点型(Floating-point Types)

浮動小数点型は、非常に大きな数値や非常に小さな小数を扱うための型です。

C#には floatdouble の2種類があります。

これらは IEEE 754 規格 に準拠しています。

floatとdoubleの比較

型キーワード.NET構造体名サイズ (bit)精度(桁数)主な用途
floatSystem.Single32約 6 ~ 9 桁グラフィックス、ゲーム開発 (GPU演算)
doubleSystem.Double64約 15 ~ 17 桁科学計算、数学的演算、一般的な実数

注意点として、浮動小数点型は「近似値」を保持する性質があるため、正確な10進数の計算には向きません。

例えば、0.1 を繰り返し加算すると、微小な誤差が蓄積されます。

浮動小数点型のコード例

C#
using System;

class Program
{
    static void Main()
    {
        // float型は数値の末尾に f または F が必要
        float piFloat = 3.14159265f;
        
        // double型(デフォルトの実数リテラル)
        double piDouble = 3.141592653589793238;

        Console.WriteLine($"Float value: {piFloat}");
        Console.WriteLine($"Double value: {piDouble}");
        
        // 浮動小数点の誤差の例
        double sum = 0;
        for (int i = 0; i < 10; i++) sum += 0.1;
        Console.WriteLine($"0.1を10回足した結果: {sum}");
        Console.WriteLine($"1.0と等しいか: {sum == 1.0}");
    }
}
実行結果
Float value: 3.141593
Double value: 3.141592653589793
0.1を10回足した結果: 0.9999999999999999
1.0と等しいか: False

10進数型(decimal型)

decimal 型は、金融計算や会計処理など、10進数ベースでの正確な計算が必要な場面で使用されます。

128ビットのサイズを持ち、浮動小数点型よりも精度が高い(約28〜29桁)のが特徴です。

なぜdecimalが必要なのか

前述の通り、double などの浮動小数点型は内部的に2進数で数値を表現するため、10進数の小数を正確に表現できない場合があります。

これに対し、decimal は10進数として数値を保持するため、人間が期待する計算結果(例:消費税計算など)を正確に導き出せます。

ただし、演算速度は double よりも遅く、パフォーマンスが重視されるゲームの物理演算などには不向きです。

decimal型のコード例

C#
using System;

class Program
{
    static void Main()
    {
        // decimal型は数値の末尾に m または M が必須
        decimal price = 100.25m;
        decimal taxRate = 0.1m;
        decimal total = price * (1 + taxRate);

        Console.WriteLine($"合計金額: {total}"); // 110.275

        // decimalでの誤差検証
        decimal sum = 0m;
        for (int i = 0; i < 10; i++) sum += 0.1m;
        Console.WriteLine($"decimalで0.1を10回足した結果: {sum}");
        Console.WriteLine($"1.0と等しいか: {sum == 1.0m}");
    }
}
実行結果
合計金額: 110.275
decimalで0.1を10回足した結果: 1.0
1.0と等しいか: True

ネイティブサイズ整数(nint, nuint)

C# 9.0から導入された nint (native int) と nuint (native unsigned int) は、実行されるプラットフォームのビット幅(32ビットまたは64ビット)に合わせてサイズが変化する型です。

  • 32ビットOS上で実行時:32ビット(intと同じ)
  • 64ビットOS上で実行時:64ビット(longと同じ)

主に低レベルの相互運用(P/Invoke)や、メモリポインタを扱うような高度なシナリオで使用されます。

通常のビジネスロジックで積極的に使用する必要はありません。

数値リテラルの表記方法

C#では、コード内の数値を読みやすくするための便利な表記法がサポートされています。

  • 桁区切り記号: アンダースコア _ を使って数値を区切ることができます(例:1_000_000)。
  • 16進数表記: 0x を接頭辞に付けます(例:0xFF)。
  • 2進数表記: 0b を接頭辞に付けます(例:0b1010)。
C#
int bigNumber = 1_000_000_000; // 10億
int hex = 0xAF;               // 175
int binary = 0b1010_1010;      // 170

型変換(キャスト)とオーバーフローの管理

異なる数値型の間で値を移動させる場合、「型変換(キャスト)」が必要になります。

暗黙的変換と明示的変換

  1. 暗黙的な変換: 小さな型から大きな型への変換(例:int から double)。データが欠落する恐れがないため、自動的に行われます。
  2. 明示的な変換: 大きな型から小さな型への変換(例:long から int)。データの欠落やオーバーフローの可能性があるため、(int) のようにキャスト演算子を記述する必要があります。

オーバーフローの検知(checked)

整数の計算で最大値を超えてしまった場合、デフォルトではエラーにならず、値が最小値にループします(ラップアラウンド)。

これを防ぎ、例外を発生させたい場合は checked キーワードを使用します。

C#
using System;

class Program
{
    static void Main()
    {
        int maxValue = int.MaxValue;
        
        // デフォルトの挙動(オーバーフローしてもエラーにならない)
        int overflowed = maxValue + 1;
        Console.WriteLine($"Default: {overflowed}");

        try
        {
            // オーバーフローをチェックする
            checked
            {
                int errorValue = maxValue + 1;
            }
        }
        catch (OverflowException)
        {
            Console.WriteLine("Overflow detected!");
        }
    }
}
実行結果
Default: -2147483648
Overflow detected!

特殊な数値:BigIntegerとComplex

標準のプリミティブ型以外にも、.NETには特殊な計算用の型が用意されています。

これらは System.Numerics 名前空間に含まれています。

  • BigInteger: メモリの許す限り、無限の大きさの整数を扱える型です。暗号化アルゴリズムなどで使用されます。
  • Complex: 実数部と虚数部を持つ複素数を扱うための型です。

数値型を扱う際のベストプラクティス

これまでの内容を踏まえ、C#での数値型選択におけるベストプラクティスをまとめます。

STEP1
整数には int を使う

原則として int を選びます。

パフォーマンス面で最もバランスが良く、標準ライブラリの多くも int を前提に設計されています。

特別な理由(外部仕様や非常に大きな値の必要性)がない限り、まずは int を用いるのが安全です。

STEP2
金額計算は decimal を使用

金額計算では必ず decimal を使うべきです。

floatdouble を金額計算に使用すると、少数の丸め誤差が蓄積して 1 円単位の端数処理で重大なバグを引き起こす原因になります。

金融用途では固定小数点に相当する decimal を用いて正確さを担保してください。

STEP3
リテラル接尾辞を明示する

読みやすさと意図の明確化のためにリテラル接尾辞を活用します。

例えば長整数には L、単精度には f、十進固定小数点には m といったサフィックスを明示することで、コンパイラや他の開発者に意図を正確に伝えられます。

STEP4
符号なし型の乱用を避ける

「負の数にならないから」という理由だけで符号なし型(uint, ulong 等)を選ばないこと。

符号なし型は他の型との演算時にキャストや意図しない振る舞いを招き、コードが複雑化しやすいです。

外部APIやプロトコルで明示的に指定されている場合を除き、符号あり型を使うのが無難です。

STEP5
重要箇所では checked を検討する

巨大な計算や外部入力を扱う箇所では checked を検討してください。

予期せぬ大きな値が入力された際にオーバーフローを検知することで、不正な計算結果のまま処理が続行されるのを防げます。

重要な算術処理にはオーバーフロー検出を有効にするのが望ましいです。

まとめ

C#の数値型は、単純な数値の保持だけでなく、計算精度、パフォーマンス、メモリ使用量のトレードオフを考慮して設計されています。

整数なら int、巨大な整数なら long、精密な実数計算なら double、そしてお金に関わる計算なら decimal という基本ルールを徹底することが、堅牢なアプリケーションへの第一歩です。

各型のビットサイズや範囲を丸暗記する必要はありませんが、それぞれの型が「どのような性質を持ち、どのようなリスク(精度不足やオーバーフロー)を抱えているか」を把握しておくことで、開発の現場でより適切な技術選定ができるようになるでしょう。

最新のC#では nint のような新しい型も登場していますが、まずは基本となる型をマスターし、状況に応じた最適な選択ができるプログラマーを目指してください。