C#におけるプログラミングにおいて、数値データ、特に「整数」の扱いは避けて通れない極めて重要な要素です。
C#は静的型付け言語であり、扱う数値の範囲やメモリ使用量に応じて適切な型を選択することが、アプリケーションのパフォーマンス向上やバグの抑制に直結します。
近年の.NETの進化に伴い、従来の32ビットや64ビットの整数型だけでなく、プラットフォームに依存する型や、さらには巨大な数値を扱うための128ビット整数型なども導入されました。
本記事では、C#で利用可能な整数型の全容を整理し、それぞれの特性や最適な選び方について、テクニカルな視点から詳しく解説します。
C#における整数型の基本分類
C#の整数型は、大きく分けて「符号付き整数(Signed Integers)」と「符号なし整数(Unsigned Integers)」の2つのグループに分類されます。
符号付き整数は正負の両方の値を扱うことができ、符号なし整数は0以上の正の値のみを扱います。
また、各型にはエイリアス(別名)が用意されており、例えば System.Int32 という .NET の型に対して int というキーワードが割り当てられています。
これらは言語仕様上、全く同じものを指しますが、一般的には int や long といった C# 独自のキーワードを使用するのが通例です。
符号付き整数型の一覧
符号付き整数は、最上位ビットを符号(プラスかマイナスか)の判定に使用します。
そのため、扱える正の最大値は、同じビット数の符号なし整数に比べて約半分になります。
sbyte(8ビット)short(16ビット)int(32ビット)long(64ビット)Int128(128ビット)
符号なし整数型の一覧
符号なし整数は、全てのビットを数値の表現に使用するため、0から始まる大きな正の値を扱うことができます。
負の値を代入しようとするとコンパイルエラー、または実行時のオーバーフローが発生します。
byte(8ビット)ushort(16ビット)uint(32ビット)ulong(64ビット)UInt128(128ビット)
整数型のサイズと範囲一覧表
各整数型のメモリサイズと、扱うことができる数値の範囲を以下の表にまとめます。
開発時には、この表を参照してデータの大きさに適した型を選択することが重要です。
| 型キーワード | .NET 型 | サイズ (バイト) | 最小値 | 最大値 |
|---|---|---|---|---|
sbyte | System.SByte | 1 | -128 | 127 |
byte | System.Byte | 1 | 0 | 255 |
short | System.Int16 | 2 | -32,768 | 32,767 |
ushort | System.UInt16 | 2 | 0 | 65,535 |
int | System.Int32 | 4 | -2,147,483,648 | 2,147,483,647 |
uint | System.UInt32 | 4 | 0 | 4,294,967,295 |
long | System.Int64 | 8 | -9,223,372,036,854,775,808 | 9,223,372,036,854,775,807 |
ulong | System.UInt64 | 8 | 0 | 18,446,744,073,709,551,615 |
Int128 | System.Int128 | 16 | 約 -1.7 × 10^38 | 約 1.7 × 10^38 |
UInt128 | System.UInt128 | 16 | 0 | 約 3.4 × 10^38 |
注意点として、Int128およびUInt128は .NET 7 以降で導入された比較的新しい型です。
それ以前の環境では標準では利用できないため、ターゲットフレームワークの確認が必要です。
各整数型の詳細解説とユースケース
標準的な整数型: int
C#において最も頻繁に利用されるのが int です。
現代のプロセッサの多くは32ビットまたは64ビットの演算に最適化されており、その中でも int は計算速度とメモリ消費のバランスが非常に優れています。
ループのカウンタ、配列のインデックス、一般的な数量のカウントなど、特別な理由がない限りは int を使用するのが推奨されます。
大規模な数値を扱う: long
int の最大値である約21億を超える可能性がある場合は、long を選択します。
例えば、全世界の人口を数える場合や、Unixエポック(1970年1月1日からの経過時間)をミリ秒やナノ秒単位で保持する場合などに適しています。
データベースの主キー(ID)としても、将来的なデータ増分を見越して long が採用されるケースが一般的です。
メモリ節約に貢献する: byte / short
画像データやバイナリファイルの読み込みなど、大量のデータを配列として保持する必要がある場合、1つひとつの要素に int を使うとメモリを大量に消費します。
例えば、色情報(RGB)は各0〜255の範囲に収まるため、byte 型が最適です。
また、センサーデータなどの小さな数値範囲のストリーム処理では short も重宝されます。
ただし、算術演算を行う際には内部的に int に拡張されることが多いため、計算効率よりもメモリ効率を優先する場合に使用します。
符号なし整数の特性と使い分け
uint や ulong は、負の値を考慮する必要がない特定のアルゴリズムや、ビット演算、ハードウェア制御のレジスタ操作などで利用されます。
ただし、C#の標準ライブラリ(BCL)の多くのメソッドが引数に int を要求するため、型変換の手間(キャスト)が発生しやすいという側面があります。
ドメインモデルにおいて「負の数はありえない」という制約を表現するために使うこともありますが、ライブラリ設計においては慎重な検討が必要です。
ネイティブ整数型 (nint と nuint)
C# 9.0 から導入された nint (native signed integer) と nuint (native unsigned integer) は、実行環境のビット数に合わせてサイズが動的に変わる特殊な整数型です。
- 32ビットOS環境: 4バイトとして動作
- 64ビットOS環境: 8バイトとして動作
これらは主に、ポインタ演算や低レイヤーの相互運用(Interop / P/Invoke)において、OSのワードサイズに合わせる必要がある場合に使用されます。
通常のアプリケーション開発で数値計算のためにこれらを選択する機会は少ないですが、パフォーマンスチューニングやシステムプログラミングにおいては不可欠な存在です。
最新の 128ビット整数 (Int128 / UInt128)
.NET 7 で導入された Int128 および UInt128 は、long の2倍のビット幅を持ちます。
これにより、これまで System.Numerics.BigInteger を使用していたケースの一部を、より高速なスタック割り当ての構造体で代用できるようになりました。
Int128 を使うべき場面
- 暗号学的な計算: 128ビットのハッシュ値や鍵の処理。
- 高精度な物理演算: 非常に大きな数値範囲を扱う科学計算。
- GUIDの数値的処理: GUID(128ビット)を数値として高速に比較・演算する場合。
BigInteger は任意精度(メモリが許す限り無限の大きさ)を扱えますが、ヒープメモリを消費するためオーバーヘッドがあります。
対して Int128 は固定長(16バイト)で値型であるため、パフォーマンス面で有利です。
実装例:各型の宣言と最大値の確認
実際に C# でこれらの型をどのように記述し、どのような値を持っているかを確認するコード例を以下に示します。
using System;
public class Program
{
public static void Main()
{
// 基本的な整数型の宣言と初期化
int standardInt = 100_000; // アンダースコアで桁区切りが可能
long largeLong = 9_000_000_000_000_000_000L; // Lサフィックスを推奨
byte smallByte = 255;
// C# 11/.NET 7以降のInt128
Int128 hugeInt = Int128.MaxValue;
Console.WriteLine($"int の範囲: {int.MinValue} ~ {int.MaxValue}");
Console.WriteLine($"long の範囲: {long.MinValue} ~ {long.MaxValue}");
Console.WriteLine($"byte の範囲: {byte.MinValue} ~ {byte.MaxValue}");
Console.WriteLine($"Int128 の最大値: {hugeInt}");
// 型変換(キャスト)の例
long myLong = 1000;
int myInt = (int)myLong; // 明示的なキャストが必要
Console.WriteLine($"キャスト後の値: {myInt}");
}
}
int の範囲: -2147483648 ~ 2147483647
long の範囲: -9223372036854775808 ~ 9223372036854775807
byte の範囲: 0 ~ 255
Int128 の最大値: 170141183460469231731687303715884105727
キャスト後の値: 1000
オーバーフローの制御:checked と unchecked
整数型を使用する際に最も注意すべきバグの一つが「オーバーフロー」です。
最大値を超えた計算を行うと、数値が最小値にループ(ラップアラウンド)してしまいます。
C# ではデフォルトでオーバーフローのチェックを行わず、計算速度を優先します(unchecked 状態)。
しかし、金融計算などの正確性が求められる処理では、オーバーフローを例外として検知すべきです。
using System;
public class OverflowTest
{
public static void Main()
{
int max = int.MaxValue;
// デフォルトの状態(オーバーフローしても例外が出ない)
int overflowed = max + 1;
Console.WriteLine($"Unchecked: {overflowed}"); // 最小値に戻る
try
{
// checked ブロック内でオーバーフローを監視
checked
{
int error = max + 1; // ここで OverflowException が発生
}
}
catch (OverflowException)
{
Console.WriteLine("Checked: オーバーフローを検知しました!");
}
}
}
Unchecked: -2147483648
Checked: オーバーフローを検知しました!
プロジェクト全体の設定で CheckForOverflowUnderflow を有効にすることも可能ですが、特定の計算箇所だけを checked キーワードで囲む手法が一般的です。
整数型を選ぶためのガイドライン
適切な整数型を選ぶための判断基準を整理します。
迷った際は以下のステップに従ってください。
基本的には int を選びます。
数値が 20億 (2,000,000,000) を超えないことが確実であれば、int が最もメモリ効率・性能面で有利です。
ID、タイムスタンプ、ファイルサイズなど、値が 20億 を超える可能性がある場合は迷わず long を選択してください。
オーバーフローのリスクを避けるため、安全側に寄せた選択が重要です。
数百万〜数千万要素の配列を扱う場合は、型を小さくすることでメモリ使用量を劇的に削減できます。
要素値の範囲が許すなら byte や short の使用を検討してください(ただし値の範囲を超えないことを確認すること)。
負の数が不要でも、ライブラリ互換性や扱いやすさの観点から原則として 符号付き型(例: int)を使うのが無難です。
ビット演算などが主目的で明確に利点がある場合に限り、符号無し型を検討してください。
64ビットの long でも足りないが、任意精度の BigInteger を使うほどではない固定長の巨大数値が必要な場合は Int128 を検討します。
用途と性能要件を踏まえて選択してください。
パフォーマンスへの影響
現代のCPUアーキテクチャでは、単一の変数として扱う場合、byte を使っても int を使っても計算速度に大きな差は出ません。
むしろ、byte や short は計算のたびに内部で int に変換されるため、微小なオーバーヘッドが生じるケースもあります。
しかし、CPUキャッシュの効率を考える場合、データ構造を小さく保つことは非常に有効です。
大量のデータをメモリに載せるシナリオでは、適切な型を選択してデータ密度を高めることで、キャッシュミスを減らし、アプリケーション全体のレスポンスを向上させることができます。
まとめ
C#の整数型は、単に「数字を扱う」以上の役割を持っています。
各型のビット数、範囲、そして .NET のバージョンによって追加された新しい型の特性を理解することは、プロフェッショナルな開発者への第一歩です。
- int: 汎用的な数値処理のデファクトスタンダード。
- long: 巨大な数値やID管理に必須。
- byte/short: メモリ効率を重視する大量データ処理用。
- nint/nuint: OSのビット数に依存するネイティブ処理用。
- Int128/UInt128: 暗号化や超高精度演算を支える最新の選択肢。
これらを適切に使い分けることで、安全かつ高速な C# プログラムを構築できるようになります。
特にオーバーフローの挙動については、予期せぬバグを防ぐために必ず checked の概念を頭に入れておきましょう。
今後の開発において、本記事の比較表やガイドラインが最適な型選びの助けとなれば幸いです。
