C# Math.Roundの使い方完全ガイド|四捨五入・小数点指定・丸め誤差までわかりやすく解説

はじめに

C#で小数を扱っていると、「小数点第2位まで丸めたい」「金額を整数にしたい」「平均値を見やすく表示したい」といった場面がよくあります。そのときに使う代表的なメソッドがMath.Roundです。

ただし、C#のMath.Roundは、単純に「0.5以上なら切り上げる」という四捨五入だけを行うメソッドではありません。既定では「銀行丸め」と呼ばれるMidpointRounding.ToEvenが使われるため、Math.Round(2.5)の結果は3ではなく2になります。Math.Roundは、指定した小数桁数、数値型、丸め方式によって結果が変わるため、仕様を理解して使うことが重要です。

この記事では、C#のMath.Roundの基本的な使い方から、小数点以下の桁数指定、一般的な四捨五入の方法、doubledecimalの違い、丸め誤差への対策、実務で使えるサンプルまでわかりやすく解説します。

1. C#のMath.Roundとは?基本の役割と使いどころ

1-1. Math.Roundでできること

Math.Roundは、数値を最も近い整数、または指定した小数点以下の桁数に丸めるためのメソッドです。たとえば、3.141593にしたり、3.14159を小数点第2位までの3.14にしたりできます。

C#
double value = 3.14159;

Console.WriteLine(Math.Round(value)); // 3
Console.WriteLine(Math.Round(value, 2)); // 3.14

Math.RoundSystem.Mathクラスに用意されています。double型とdecimal型の両方に対応しており、丸め方式を指定できるオーバーロードもあります。

1-2. 四捨五入と丸め処理の違い

「四捨五入」は丸め処理の一種です。一般的には、対象の桁の次の桁が5以上なら切り上げ、4以下なら切り捨てる処理を指します。

一方で「丸め処理」は、より広い意味を持ちます。たとえば、次のような処理も丸めに含まれます。

C#
Math.Round(3.5);     // 最も近い値に丸める
Math.Floor(3.9); // 切り捨て方向に丸める
Math.Ceiling(3.1); // 切り上げ方向に丸める
Math.Truncate(3.9); // 小数部を切り捨てる

つまり、Math.Roundは「近い値に丸める」メソッドであり、必ずしも学校で習う一般的な四捨五入と同じ結果になるとは限りません。

1-3. int・double・decimalで丸めが必要になる場面

intは整数型なので、小数点以下の値を持ちません。そのため、intそのものを丸める必要はほとんどありません。

丸めが必要になるのは、主にdoubledecimalのような小数を扱う型です。

C#
double average = 83.666666;
decimal price = 1234.567m;

double roundedAverage = Math.Round(average, 2);
decimal roundedPrice = Math.Round(price, 0);

doubleは科学計算や一般的な実数計算でよく使われ、decimalは金額計算など、10進数としての正確さが重要な場面でよく使われます。C#ではdoubleは最大15〜17桁程度、decimalは28〜29桁程度の精度を持つ型として定義されています。

1-4. 金額計算・集計処理・表示用フォーマットでの活用例

Math.Roundは、次のような実務的な場面でよく使われます。

C#
// 消費税計算
decimal price = 1980m;
decimal tax = Math.Round(price * 0.10m, 0, MidpointRounding.AwayFromZero);

// 平均値を小数点第2位まで丸める
double average = 86.666666;
double roundedAverage = Math.Round(average, 2);

// パーセンテージを小数点第1位まで丸める
double rate = 0.12345;
double percent = Math.Round(rate * 100, 1);

ただし、計算そのものを丸めるのか、画面表示だけを丸めるのかは分けて考える必要があります。表示桁数を整えたいだけなら、Math.RoundではなくToStringによる書式指定のほうが適している場合もあります。

2. Math.Roundの基本的な使い方

2-1. Math.Round(value)の基本構文

もっとも基本的な書き方は、次の形です。

C#
Math.Round(value);

valueに丸めたい数値を指定します。この書き方では、小数点以下を丸めて、最も近い整数値を返します。

C#
Console.WriteLine(Math.Round(3.2));  // 3
Console.WriteLine(Math.Round(3.8)); // 4

ただし、戻り値はintではありません。doubleを渡した場合はdoubledecimalを渡した場合はdecimalが返ります。Round(Double)は最も近い整数値に丸めますが、戻り値の型は整数型ではなくdoubleです。

2-2. double型を丸めるサンプルコード

double型の値を丸める例を見てみましょう。

C#
double a = 12.3;
double b = 12.6;
double c = 12.5;

Console.WriteLine(Math.Round(a)); // 12
Console.WriteLine(Math.Round(b)); // 13
Console.WriteLine(Math.Round(c)); // 12

12.513ではなく12になる点に注意してください。これはMath.Roundの既定の丸め方式がMidpointRounding.ToEven、つまり最も近い偶数に丸める方式だからです。

2-3. decimal型を丸めるサンプルコード

decimal型を使う場合は、数値リテラルの末尾にmを付けます。

C#
decimal price = 123.456m;

decimal rounded = Math.Round(price, 2);

Console.WriteLine(rounded); // 123.46

decimalは、金額や税率など10進数として扱いたい値に適しています。C#のドキュメントでも、通貨額や利率など、小数点以下の桁数によって精度が重要になる値にはdecimalが適していると説明されています。

2-4. 戻り値の型と代入時の注意点

Math.Roundの戻り値は、引数の型によって変わります。

C#
double d = 10.75;
decimal m = 10.75m;

double roundedDouble = Math.Round(d);
decimal roundedDecimal = Math.Round(m);

次のように、intへ直接代入することはできません。

C#
double value = 10.75;

// コンパイルエラー
// int result = Math.Round(value);

整数型にしたい場合は、明示的にキャストします。

C#
double value = 10.75;

int result = (int)Math.Round(value);

Console.WriteLine(result); // 11

ただし、大きすぎる値をintに変換すると範囲外になる可能性があります。実務では、値の範囲を確認したうえでキャストしましょう。

2-5. 負の数をMath.Roundで丸めた場合の挙動

負の数もMath.Roundで丸められます。

C#
Console.WriteLine(Math.Round(-3.2)); // -3
Console.WriteLine(Math.Round(-3.8)); // -4
Console.WriteLine(Math.Round(-3.5)); // -4
Console.WriteLine(Math.Round(-2.5)); // -2

-3.5-4-2.5-2になります。どちらも「近い偶数」に丸められているためです。

一般的な感覚では、-2.5-3になってほしいと感じるかもしれません。その場合は、後述するMidpointRounding.AwayFromZeroを指定します。

3. 小数点以下の桁数を指定して丸める方法

3-1. Math.Round(value, digits)の使い方

小数点以下の桁数を指定したい場合は、次の形を使います。

C#
Math.Round(value, digits);

digitsには、残したい小数点以下の桁数を指定します。

C#
double value = 3.14159;

Console.WriteLine(Math.Round(value, 0)); // 3
Console.WriteLine(Math.Round(value, 1)); // 3.1
Console.WriteLine(Math.Round(value, 2)); // 3.14
Console.WriteLine(Math.Round(value, 3)); // 3.142

Math.Round(value, 2)は、小数点第2位まで残すという意味です。小数点第3位を見て丸める、と覚えるとわかりやすいでしょう。

3-2. 小数点第1位・第2位・第3位で丸める例

桁数ごとの違いを確認してみます。

C#
double value = 123.4567;

Console.WriteLine(Math.Round(value, 1)); // 123.5
Console.WriteLine(Math.Round(value, 2)); // 123.46
Console.WriteLine(Math.Round(value, 3)); // 123.457

digits1を指定すると小数点第1位まで、2を指定すると小数点第2位まで、3を指定すると小数点第3位まで残ります。

decimalでも同じように書けます。

C#
decimal value = 123.4567m;

Console.WriteLine(Math.Round(value, 1)); // 123.5
Console.WriteLine(Math.Round(value, 2)); // 123.46
Console.WriteLine(Math.Round(value, 3)); // 123.457

3-3. digitsに指定できる範囲と例外

digitsに指定できる範囲は、doubledecimalで異なります。

doubleの場合、digits0から15までです。decimalの場合、decimals0から28までです。範囲外の値を指定すると、ArgumentOutOfRangeExceptionが発生します。

C#
double value = 1.23456;

// OK
Console.WriteLine(Math.Round(value, 15));

// 例外
// Console.WriteLine(Math.Round(value, 16));

decimalの場合は次のようになります。

C#
decimal value = 1.23456m;

// OK
Console.WriteLine(Math.Round(value, 28));

// 例外
// Console.WriteLine(Math.Round(value, 29));

入力値や設定値から桁数を受け取る場合は、範囲チェックを入れておくと安全です。

C#
int digits = 2;

if (digits < 0 || digits > 15)
{
throw new ArgumentOutOfRangeException(nameof(digits));
}

double rounded = Math.Round(1.23456, digits);

3-4. 小数点以下を0桁にする場合の書き方

小数点以下を残さず整数相当の値に丸めたい場合は、次のどちらでも書けます。

C#
double value = 123.456;

Console.WriteLine(Math.Round(value)); // 123
Console.WriteLine(Math.Round(value, 0)); // 123

Math.Round(value)は、小数点以下を丸めて整数値にする基本形です。Math.Round(value, 0)は、小数点以下0桁まで残すという意味です。

丸め方式も指定したい場合は、次のように書きます。

C#
double value = 123.5;

Console.WriteLine(Math.Round(value, 0, MidpointRounding.AwayFromZero)); // 124

3-5. 表示桁数を揃えたい場合はToStringも使う

Math.Roundは数値そのものを丸めるメソッドです。表示上の桁数を揃えるメソッドではありません。

たとえば、次のコードでは1.20と表示されるとは限りません。

C#
double value = 1.2;
double rounded = Math.Round(value, 2);

Console.WriteLine(rounded); // 1.2

小数点第2位まで表示したい場合は、ToStringを使います。

C#
double value = 1.2;

Console.WriteLine(value.ToString("F2")); // 1.20

Math.Round(value, 2)は計算用の値を丸める処理、value.ToString("F2")は表示用の文字列を作る処理です。この2つは目的が違います。

4. Math.Roundは普通の四捨五入ではない?標準の丸め仕様を理解する

4-1. C#のMath.Roundは既定で銀行丸めになる

C#のMath.Roundで最もつまずきやすいポイントは、既定の丸め方式です。

Math.Roundは、丸め方式を指定しない場合、MidpointRounding.ToEvenを使います。これは「最近接偶数への丸め」または「銀行丸め」と呼ばれます。公式ドキュメントでも、Round(Double)Round(Decimal)は中間値を最も近い偶数に丸めると説明されています。

C#
Console.WriteLine(Math.Round(1.5)); // 2
Console.WriteLine(Math.Round(2.5)); // 2
Console.WriteLine(Math.Round(3.5)); // 4
Console.WriteLine(Math.Round(4.5)); // 4

「.5なら必ず切り上げ」と思っていると、2.54.5の結果に違和感を持つはずです。

4-2. 2.5が2になる理由

2.5は、23のちょうど中間にあります。Math.Roundの既定では、こうした中間値を偶数側に丸めます。

C#
Console.WriteLine(Math.Round(2.5)); // 2

2は偶数、3は奇数です。そのため、2.52になります。

同じ考え方で、3.534の中間ですが、偶数である4に丸められます。

C#
Console.WriteLine(Math.Round(3.5)); // 4

4-3. MidpointRounding.ToEvenとは

MidpointRounding.ToEvenは、丸め対象の値が2つの候補のちょうど中間にある場合、偶数側に丸める方式です。

C#
Console.WriteLine(Math.Round(2.5, MidpointRounding.ToEven)); // 2
Console.WriteLine(Math.Round(3.5, MidpointRounding.ToEven)); // 4
Console.WriteLine(Math.Round(4.5, MidpointRounding.ToEven)); // 4
Console.WriteLine(Math.Round(5.5, MidpointRounding.ToEven)); // 6

この方式は、常に一方向へ丸めることによる偏りを抑えやすいという特徴があります。そのため、統計処理や大量の集計処理では合理的な選択になることがあります。公式ドキュメントでも、最近接偶数への丸めは複数の丸め操作における丸め誤差の偏りを減らすと説明されています。

4-4. 一般的な四捨五入との違い

一般的な四捨五入では、次のような結果を期待することが多いでしょう。

C#
1.5 -> 2
2.5 -> 3
3.5 -> 4
4.5 -> 5

しかし、Math.Roundの既定では次のようになります。

C#
1.5 -> 2
2.5 -> 2
3.5 -> 4
4.5 -> 4

違いが出るのは、ちょうど.5のような中間値のときです。中間値でない場合は、通常の「近い値に丸める」処理になります。

C#
Console.WriteLine(Math.Round(2.4)); // 2
Console.WriteLine(Math.Round(2.6)); // 3

4-5. 期待した結果にならない代表例

Math.Roundでよくある混乱は、次のようなケースです。

C#
Console.WriteLine(Math.Round(2.5));  // 2
Console.WriteLine(Math.Round(12.5)); // 12
Console.WriteLine(Math.Round(14.5)); // 14

「5なのに切り上がらない」と感じるかもしれませんが、これはバグではなく仕様です。

一般的な四捨五入にしたい場合は、丸め方式としてMidpointRounding.AwayFromZeroを明示します。

C#
Console.WriteLine(Math.Round(2.5, MidpointRounding.AwayFromZero));  // 3
Console.WriteLine(Math.Round(12.5, MidpointRounding.AwayFromZero)); // 13
Console.WriteLine(Math.Round(14.5, MidpointRounding.AwayFromZero)); // 15

5. 一般的な四捨五入をしたい場合の指定方法

5-1. MidpointRounding.AwayFromZeroの使い方

一般的な「5以上なら切り上げ」に近い動きをさせたい場合は、MidpointRounding.AwayFromZeroを指定します。

C#
Math.Round(value, MidpointRounding.AwayFromZero);

AwayFromZeroは、直訳すると「ゼロから離れる方向」という意味です。正の数では大きい方向、負の数では小さい方向に丸められます。

C#
Console.WriteLine(Math.Round(2.5, MidpointRounding.AwayFromZero));  // 3
Console.WriteLine(Math.Round(-2.5, MidpointRounding.AwayFromZero)); // -3

MidpointRounding.AwayFromZeroは、中間値をゼロから離れた次の値に丸める方式として定義されています。

5-2. Math.Round(value, digits, MidpointRounding.AwayFromZero)のサンプル

小数点以下の桁数と丸め方式を両方指定する場合は、次のように書きます。

C#
double value = 1.235;

double rounded = Math.Round(value, 2, MidpointRounding.AwayFromZero);

Console.WriteLine(rounded); // 1.24

decimalでも同じです。

C#
decimal value = 1.235m;

decimal rounded = Math.Round(value, 2, MidpointRounding.AwayFromZero);

Console.WriteLine(rounded); // 1.24

金額計算などで「小数点第2位まで一般的な四捨五入をしたい」という場合は、次のようにdecimalAwayFromZeroを組み合わせるのがわかりやすいです。

C#
decimal amount = 1234.565m;

decimal rounded = Math.Round(amount, 2, MidpointRounding.AwayFromZero);

Console.WriteLine(rounded); // 1234.57

5-3. 正の数と負の数で結果がどう変わるか

AwayFromZeroは、正の数では大きい方向、負の数では小さい方向に丸めます。

C#
Console.WriteLine(Math.Round(2.5, MidpointRounding.AwayFromZero));   // 3
Console.WriteLine(Math.Round(-2.5, MidpointRounding.AwayFromZero)); // -3

ToEvenと比較すると、違いがわかりやすくなります。

C#
Console.WriteLine(Math.Round(2.5, MidpointRounding.ToEven));   // 2
Console.WriteLine(Math.Round(-2.5, MidpointRounding.ToEven)); // -2

Console.WriteLine(Math.Round(2.5, MidpointRounding.AwayFromZero)); // 3
Console.WriteLine(Math.Round(-2.5, MidpointRounding.AwayFromZero)); // -3

負の数を扱う場合は、「四捨五入」という言葉だけでは仕様が曖昧になりやすいです。業務システムでは、負の値をどう扱うかも含めて明確に決めておきましょう。

5-4. ToEvenとAwayFromZeroの比較表

ToEvenAwayFromZero
1.522
2.523
3.544
4.545
-1.5-2-2
-2.5-2-3
-3.5-4-4
-4.5-4-5

コードで確認すると、次のようになります。

C#
double[] values = { 1.5, 2.5, 3.5, 4.5, -1.5, -2.5, -3.5, -4.5 };

foreach (double value in values)
{
Console.WriteLine(
$"{value}: ToEven={Math.Round(value, MidpointRounding.ToEven)}, " +
$"AwayFromZero={Math.Round(value, MidpointRounding.AwayFromZero)}"
);
}

5-5. 業務システムでどちらを選ぶべきか

業務システムでは、「どちらが正しいか」ではなく「業務ルールに合っているか」で選ぶ必要があります。

会計、統計、大量集計など、丸めの偏りを抑えたい場面ではToEvenが適する場合があります。一方、請求金額、消費税、帳票、ユーザーが一般的な四捨五入を期待する画面では、AwayFromZeroを明示したほうがわかりやすい場合があります。

重要なのは、丸め方式をコード上で明示することです。

C#
// 意図が伝わりにくい
var amount1 = Math.Round(amount, 0);

// 意図が伝わりやすい
var amount2 = Math.Round(amount, 0, MidpointRounding.AwayFromZero);

仕様書やテストケースにも、「小数点以下は四捨五入」だけでなく、「MidpointRounding.AwayFromZeroで丸める」のように具体的に書くと、認識違いを防ぎやすくなります。

6. Math.Roundのオーバーロード一覧

6-1. Math.Round(double)

double型の値を、最も近い整数値に丸めます。

C#
double value = 3.6;

double result = Math.Round(value);

Console.WriteLine(result); // 4

既定の丸め方式はToEvenです。

C#
Console.WriteLine(Math.Round(2.5)); // 2

6-2. Math.Round(double, int)

double型の値を、指定した小数点以下の桁数に丸めます。

C#
double value = 3.14159;

double result = Math.Round(value, 2);

Console.WriteLine(result); // 3.14

digitsには0から15までを指定できます。範囲外の場合はArgumentOutOfRangeExceptionです。

6-3. Math.Round(double, MidpointRounding)

double型の値を整数値に丸めるとき、丸め方式を指定できます。

C#
double value = 2.5;

Console.WriteLine(Math.Round(value, MidpointRounding.ToEven)); // 2
Console.WriteLine(Math.Round(value, MidpointRounding.AwayFromZero)); // 3

小数点以下の桁数は指定せず、整数相当の値に丸めたい場合に使います。

6-4. Math.Round(double, int, MidpointRounding)

double型の値を、指定した小数点以下の桁数と丸め方式で丸めます。

C#
double value = 1.235;

double result = Math.Round(value, 2, MidpointRounding.AwayFromZero);

Console.WriteLine(result);

doubleは2進浮動小数点数であるため、10進数の小数を正確に表せない場合があります。金額計算では、可能であればdecimalを検討しましょう。

6-5. Math.Round(decimal)

decimal型の値を、最も近い整数値に丸めます。

C#
decimal value = 123.6m;

decimal result = Math.Round(value);

Console.WriteLine(result); // 124

既定の丸め方式はToEvenです。

C#
Console.WriteLine(Math.Round(122.5m)); // 122
Console.WriteLine(Math.Round(123.5m)); // 124

6-6. Math.Round(decimal, int)

decimal型の値を、指定した小数点以下の桁数に丸めます。

C#
decimal value = 123.456m;

decimal result = Math.Round(value, 2);

Console.WriteLine(result); // 123.46

decimaldecimalsには0から28までを指定できます。範囲外の場合はArgumentOutOfRangeExceptionが発生します。

6-7. Math.Round(decimal, MidpointRounding)

decimal型の値を整数値に丸めるとき、丸め方式を指定できます。

C#
decimal value = 2.5m;

Console.WriteLine(Math.Round(value, MidpointRounding.ToEven)); // 2
Console.WriteLine(Math.Round(value, MidpointRounding.AwayFromZero)); // 3

金額を整数に丸める処理などで使いやすいオーバーロードです。

6-8. Math.Round(decimal, int, MidpointRounding)

decimal型の値を、指定した小数点以下の桁数と丸め方式で丸めます。

C#
decimal value = 123.455m;

decimal result = Math.Round(value, 2, MidpointRounding.AwayFromZero);

Console.WriteLine(result); // 123.46

金額計算や税計算では、この形式を使うことが多いです。

C#
decimal tax = Math.Round(1980m * 0.10m, 0, MidpointRounding.AwayFromZero);

Console.WriteLine(tax); // 198

6-9. 用途別に選ぶべきオーバーロード

用途推奨する書き方
整数に丸めたいMath.Round(value)
小数点以下の桁数を指定したいMath.Round(value, digits)
一般的な四捨五入にしたいMath.Round(value, MidpointRounding.AwayFromZero)
桁数と丸め方式を明示したいMath.Round(value, digits, MidpointRounding.AwayFromZero)
金額を扱うdecimal版のMath.Round
丸め方式の誤解を避けたいMidpointRoundingを明示する

実務では、丸め方式を指定しないMath.Round(value, digits)よりも、次のように明示したほうが安全です。

C#
decimal rounded = Math.Round(value, 2, MidpointRounding.AwayFromZero);

7. doubleとdecimalの違いと丸め誤差への対策

7-1. doubleで丸め誤差が起きる理由

doubleは2進数で小数を表現する浮動小数点型です。そのため、0.11.005のような10進数の小数を正確に表せない場合があります。

C#
double value = 0.1 + 0.2;

Console.WriteLine(value); // 0.30000000000000004 のように表示されることがある

これはC#に限った不具合ではなく、2進浮動小数点数の性質です。MicrosoftのC#リファレンスでも、doublefloatでは0.1を正確に表せず、10進データに使うと予期しない丸めエラーが発生する可能性があると説明されています。

7-2. 1.005を小数点第2位で丸めると期待通りにならない例

よくある例が1.005です。

C#
double value = 1.005;

Console.WriteLine(Math.Round(value, 2, MidpointRounding.AwayFromZero));

「1.01」になると思っていても、環境や値の内部表現によっては期待と異なる結果に見えることがあります。これは、double1.005が内部的に厳密な1.005として保持されているとは限らないためです。

decimalを使うと、10進数として扱いやすくなります。

C#
decimal value = 1.005m;

Console.WriteLine(Math.Round(value, 2, MidpointRounding.AwayFromZero)); // 1.01

金額計算のように10進数の正確さが重要な処理では、doubleではなくdecimalを使うほうが安全です。

7-3. 金額計算ではdecimalを使うべき理由

金額計算では、decimalを使うのが基本です。

C#
decimal price = 1000m;
decimal taxRate = 0.10m;

decimal tax = Math.Round(price * taxRate, 0, MidpointRounding.AwayFromZero);

Console.WriteLine(tax); // 100

decimalは10進数の小数を扱うのに適しており、通貨額や利率のような値に向いています。公式ドキュメントでも、decimalは小数点以下の桁数によって必要な精度が決まる値、特に金融アプリケーションでよく使われる値に適していると説明されています。

7-4. doubleを使う場合の注意点

doubleが悪いわけではありません。科学技術計算、座標計算、統計処理、グラフ描画など、広い範囲の実数を効率よく扱いたい場面ではdoubleがよく使われます。

ただし、次の点には注意が必要です。

C#
double value = 2.675;

Console.WriteLine(Math.Round(value, 2));

見た目には2.675でも、内部表現ではわずかに小さい値や大きい値になっている可能性があります。その結果、丸め結果が直感とずれることがあります。

doubleを使う場合は、次のような考え方が重要です。

C#
// 表示目的なら書式指定を使う
Console.WriteLine(value.ToString("F2"));

// 金額ならdecimalを使う
decimal amount = 2.675m;
Console.WriteLine(Math.Round(amount, 2, MidpointRounding.AwayFromZero));

7-5. 丸め誤差を避ける実装の考え方

丸め誤差を避けるには、次の方針を意識しましょう。

方針内容
金額はdecimalを使う10進数としての正確さを優先する
丸め方式を明示するToEvenAwayFromZeroかを明確にする
計算途中で丸めすぎない最終結果で丸める
表示目的ならToStringを使う値の変更と表示の整形を分ける
境界値をテストする1.0052.5-2.5などを確認する

たとえば、次のように計算途中で何度も丸めると、最終結果に差が出ることがあります。

C#
decimal a = Math.Round(10.005m, 2, MidpointRounding.AwayFromZero);
decimal b = Math.Round(20.005m, 2, MidpointRounding.AwayFromZero);

decimal total = a + b;

可能であれば、計算途中では丸めず、最終的に必要なタイミングで丸めます。

C#
decimal total = 10.005m + 20.005m;
decimal roundedTotal = Math.Round(total, 2, MidpointRounding.AwayFromZero);

8. Math.Roundと他の丸め系メソッドの違い

8-1. Math.Floorとの違い

Math.Floorは、指定した値以下の最大の整数値を返します。簡単にいうと、負の方向へ丸めます。

C#
Console.WriteLine(Math.Floor(3.9));   // 3
Console.WriteLine(Math.Floor(-3.1)); // -4

正の数だけを見ると「小数点以下切り捨て」のように見えますが、負の数では結果が変わる点に注意してください。

C#
Console.WriteLine(Math.Floor(-3.9)); // -4

8-2. Math.Ceilingとの違い

Math.Ceilingは、指定した値以上の最小の整数値を返します。簡単にいうと、正の方向へ丸めます。

C#
Console.WriteLine(Math.Ceiling(3.1));   // 4
Console.WriteLine(Math.Ceiling(-3.9)); // -3

正の数では切り上げ、負の数ではゼロに近づく方向になる場合があります。

8-3. Math.Truncateとの違い

Math.Truncateは、小数部を単純に取り除きます。ゼロ方向へ丸めると考えるとわかりやすいです。

C#
Console.WriteLine(Math.Truncate(3.9));   // 3
Console.WriteLine(Math.Truncate(-3.9)); // -3

Floorと似ているように見えますが、負の数で結果が異なります。

C#
Console.WriteLine(Math.Floor(-3.9));     // -4
Console.WriteLine(Math.Truncate(-3.9)); // -3

8-4. int型へのキャストとの違い

intへのキャストは、小数部を切り捨てます。これはMath.Truncateに近い動きです。

C#
double value = 3.9;

Console.WriteLine((int)value); // 3

負の数でも、ゼロ方向に小数部が取り除かれます。

C#
double value = -3.9;

Console.WriteLine((int)value); // -3

一方、Math.Roundは最も近い値に丸めます。

C#
Console.WriteLine(Math.Round(3.9)); // 4
Console.WriteLine((int)3.9); // 3

丸めたいのか、小数部を捨てたいのかで使い分けましょう。

8-5. Round・Floor・Ceiling・Truncateの使い分け表

メソッド動き3.9-3.9
Math.Round最も近い値に丸める4-4
Math.Floor負の方向へ丸める3-4
Math.Ceiling正の方向へ丸める4-3
Math.Truncate小数部を取り除く3-3
(int)キャスト小数部を取り除く3-3

用途ごとの目安は次のとおりです。

C#
// 通常の丸め
Math.Round(value);

// 常に下方向へ
Math.Floor(value);

// 常に上方向へ
Math.Ceiling(value);

// 小数部を捨てる
Math.Truncate(value);

// 整数型に変換する
(int)value;

9. Math.Roundでよくあるエラー・つまずきポイント

9-1. 「四捨五入したのに結果が違う」と感じる原因

最も多い原因は、Math.Roundの既定の丸め方式がToEvenであることです。

C#
Console.WriteLine(Math.Round(2.5)); // 2

一般的な四捨五入を期待している場合は、次のように書きます。

C#
Console.WriteLine(Math.Round(2.5, MidpointRounding.AwayFromZero)); // 3

小数点以下の桁数も指定するなら、次の形です。

C#
Console.WriteLine(Math.Round(2.345, 2, MidpointRounding.AwayFromZero));

「なぜこの結果になったのか」を読み手が理解できるよう、丸め方式はなるべく明示しましょう。

9-2. 小数点以下の0が表示されない理由

Math.Roundで小数点第2位まで丸めても、末尾の0は表示されないことがあります。

C#
double value = 1.2;
double rounded = Math.Round(value, 2);

Console.WriteLine(rounded); // 1.2

これは、数値としては1.21.20も同じ値だからです。表示上1.20にしたい場合は、ToString("F2")を使います。

C#
double value = 1.2;

Console.WriteLine(value.ToString("F2")); // 1.20

つまり、Math.Roundは数値の丸め、ToStringは表示形式の指定です。

9-3. digitsの指定でArgumentOutOfRangeExceptionが出るケース

digitsに範囲外の値を指定すると、ArgumentOutOfRangeExceptionが発生します。

C#
double value = 1.234;

// doubleでは0〜15の範囲
// Math.Round(value, 16); // 例外

decimalの場合も範囲外は例外です。

C#
decimal value = 1.234m;

// decimalでは0〜28の範囲
// Math.Round(value, 29); // 例外

外部入力から桁数を受け取る場合は、必ずチェックしましょう。

C#
int digits = GetDigits();

if (digits < 0 || digits > 28)
{
throw new ArgumentOutOfRangeException(nameof(digits));
}

decimal rounded = Math.Round(123.456m, digits);

9-4. decimalとdoubleを混在させたときの注意点

decimaldoubleをそのまま混在させて計算することはできません。

C#
decimal price = 1000m;
double rate = 0.1;

// コンパイルエラー
// var tax = price * rate;

どちらかに明示的に変換する必要があります。

C#
decimal price = 1000m;
decimal rate = 0.10m;

decimal tax = price * rate;

金額計算であれば、最初から最後までdecimalで揃えるのが安全です。

C#
decimal price = 1000m;
decimal taxRate = 0.10m;

decimal tax = Math.Round(price * taxRate, 0, MidpointRounding.AwayFromZero);

C#では、decimalfloatまたはdoubleを算術演算で混在させる場合、明示的な変換が必要です。

9-5. 丸め処理と文字列フォーマットを混同しない

丸め処理と文字列フォーマットは目的が違います。

C#
double value = 1.23456;

// 数値を丸める
double rounded = Math.Round(value, 2);

// 表示を整える
string text = value.ToString("F2");

roundedは計算に使う数値です。textは表示用の文字列です。

C#
Console.WriteLine(rounded); // 1.23
Console.WriteLine(text); // 1.23

同じように見える場合もありますが、使う目的は別です。

金額表示では、計算用のdecimalと表示用の文字列を分けると、意図が明確になります。

C#
decimal amount = 1234.5m;

decimal roundedAmount = Math.Round(amount, 0, MidpointRounding.AwayFromZero);
string displayAmount = roundedAmount.ToString("N0");

Console.WriteLine(displayAmount); // 1,235

10. 実務で使えるMath.Roundのサンプル集

10-1. 消費税計算で小数点以下を丸める

消費税計算では、金額をdecimalで扱い、丸め方式を明示するのがおすすめです。

C#
decimal price = 1980m;
decimal taxRate = 0.10m;

decimal tax = Math.Round(price * taxRate, 0, MidpointRounding.AwayFromZero);

Console.WriteLine(tax); // 198

税込金額を求める場合は次のように書けます。

C#
decimal price = 1980m;
decimal taxRate = 0.10m;

decimal tax = Math.Round(price * taxRate, 0, MidpointRounding.AwayFromZero);
decimal total = price + tax;

Console.WriteLine(total); // 2178

実際の業務では、税区分、端数処理の単位、端数処理のタイミングがシステムごとに異なるため、仕様に合わせて実装しましょう。

10-2. 平均値を小数点第2位まで表示する

平均値を小数点第2位まで丸める例です。

C#
double[] scores = { 80, 90, 85, 92 };

double average = scores.Average();
double roundedAverage = Math.Round(average, 2);

Console.WriteLine(roundedAverage);

表示桁数を必ず2桁にしたい場合は、ToString("F2")を使います。

C#
Console.WriteLine(roundedAverage.ToString("F2"));

計算用の丸めと表示用のフォーマットを分けることで、意図が明確になります。

10-3. パーセンテージを丸める

割合をパーセンテージ表示する場合は、100倍してから丸めます。

C#
double rate = 0.12345;

double percent = Math.Round(rate * 100, 1, MidpointRounding.AwayFromZero);

Console.WriteLine(percent); // 12.3
Console.WriteLine(percent.ToString("F1") + "%"); // 12.3%

小数点第2位まで表示したい場合は、次のようにします。

C#
double rate = 0.12345;

double percent = Math.Round(rate * 100, 2, MidpointRounding.AwayFromZero);

Console.WriteLine(percent.ToString("F2") + "%"); // 12.35%

10-4. 金額を整数に丸める

金額を整数に丸める場合は、decimalMidpointRounding.AwayFromZeroを使うとわかりやすいです。

C#
decimal amount = 1234.5m;

decimal rounded = Math.Round(amount, 0, MidpointRounding.AwayFromZero);

Console.WriteLine(rounded); // 1235

整数型として扱いたい場合は、丸めたあとに変換します。

C#
decimal amount = 1234.5m;

int rounded = (int)Math.Round(amount, 0, MidpointRounding.AwayFromZero);

Console.WriteLine(rounded); // 1235

ただし、intの範囲を超える可能性がある場合は、longdecimalのまま扱うことも検討しましょう。

10-5. LINQの集計結果を丸める

LINQで集計した結果を丸める例です。

C#
var items = new[]
{
new { Name = "A", Price = 100m },
new { Name = "B", Price = 250m },
new { Name = "C", Price = 380m }
};

decimal averagePrice = items.Average(x => x.Price);
decimal roundedAverage = Math.Round(averagePrice, 0, MidpointRounding.AwayFromZero);

Console.WriteLine(roundedAverage);

カテゴリ別に平均を求めて丸める例です。

C#
var sales = new[]
{
new { Category = "Food", Amount = 120m },
new { Category = "Food", Amount = 150m },
new { Category = "Book", Amount = 980m },
new { Category = "Book", Amount = 1280m }
};

var result = sales
.GroupBy(x => x.Category)
.Select(g => new
{
Category = g.Key,
Average = Math.Round(g.Average(x => x.Amount), 0, MidpointRounding.AwayFromZero)
});

foreach (var item in result)
{
Console.WriteLine($"{item.Category}: {item.Average}");
}

集計処理では、個々の値を丸めてから集計するのか、集計後に丸めるのかで結果が変わります。仕様に合わせて、丸めるタイミングを明確にしましょう。

11. Math.Roundを使うときのベストプラクティス

11-1. 丸め方式を明示して意図をわかりやすくする

Math.Roundは、丸め方式を省略するとToEvenになります。これを知っている人ばかりではないため、業務コードでは丸め方式を明示するのがおすすめです。

C#
// 意図が曖昧
decimal rounded = Math.Round(amount, 0);

// 意図が明確
decimal rounded = Math.Round(amount, 0, MidpointRounding.AwayFromZero);

特に、金額、数量、請求、ポイント、割引率など、ユーザーや業務担当者が結果を確認する値では、丸め方式の明示が重要です。

11-2. 金額計算ではdecimalを優先する

金額計算では、基本的にdecimalを使いましょう。

C#
decimal price = 1000m;
decimal rate = 0.08m;

decimal tax = Math.Round(price * rate, 0, MidpointRounding.AwayFromZero);

doubleは2進浮動小数点数であり、10進小数を正確に表せないことがあります。decimalは通貨額や利率など、小数点以下の精度が重要な値に適しています。

11-3. 計算途中で丸めすぎない

計算途中で丸めると、誤差が積み重なる場合があります。

C#
decimal subtotal1 = Math.Round(100.005m, 2, MidpointRounding.AwayFromZero);
decimal subtotal2 = Math.Round(200.005m, 2, MidpointRounding.AwayFromZero);

decimal total = subtotal1 + subtotal2;

可能であれば、内部計算では精度を保ち、最終的な出力や確定値のタイミングで丸めます。

C#
decimal total = 100.005m + 200.005m;
decimal roundedTotal = Math.Round(total, 2, MidpointRounding.AwayFromZero);

ただし、税計算や明細単位の端数処理のように、途中で丸めること自体が業務ルールになっている場合もあります。その場合は仕様に従いましょう。

11-4. 表示用と計算用の丸めを分ける

表示用に小数点以下2桁で見せたいだけなら、ToString("F2")で十分な場合があります。

C#
decimal amount = 1234.5m;

Console.WriteLine(amount.ToString("F2")); // 1234.50

一方、計算結果として値そのものを丸めたい場合はMath.Roundを使います。

C#
decimal amount = 1234.567m;

decimal rounded = Math.Round(amount, 2, MidpointRounding.AwayFromZero);

Console.WriteLine(rounded); // 1234.57

「数値を変える処理」と「見た目を整える処理」を混同しないことが大切です。

11-5. テストケースで境界値を確認する

丸め処理は、境界値のテストが重要です。特に、.5を含む値、負の数、桁数指定、doubleの誤差が出やすい値を確認しましょう。

C#
Console.WriteLine(Math.Round(2.5, MidpointRounding.ToEven));       // 2
Console.WriteLine(Math.Round(2.5, MidpointRounding.AwayFromZero)); // 3

Console.WriteLine(Math.Round(-2.5, MidpointRounding.ToEven)); // -2
Console.WriteLine(Math.Round(-2.5, MidpointRounding.AwayFromZero)); // -3

Console.WriteLine(Math.Round(1.005m, 2, MidpointRounding.AwayFromZero)); // 1.01

テストすべき代表例は次のとおりです。

テスト値確認ポイント
2.5ToEvenAwayFromZeroの違い
-2.5負の中間値の扱い
1.005小数点第2位への丸め
0.1 + 0.2doubleの誤差
999999999.5大きな数の丸め
digits = -1例外
digits = 16doubleでの例外
decimals = 29decimalでの例外

12. C# Math.Roundに関するよくある質問

12-1. Math.Roundで必ず四捨五入できますか?

丸め方式を指定しない場合、一般的な四捨五入にはなりません。Math.Roundの既定はMidpointRounding.ToEvenです。

一般的な四捨五入に近い処理をしたい場合は、MidpointRounding.AwayFromZeroを指定します。

C#
double value = 2.5;

Console.WriteLine(Math.Round(value)); // 2
Console.WriteLine(Math.Round(value, MidpointRounding.AwayFromZero)); // 3

業務コードでは、丸め方式を省略せずに明示するのがおすすめです。

12-2. 小数点第2位まで表示するにはどうすればいいですか?

数値を小数点第2位まで丸めたい場合は、Math.Round(value, 2)を使います。

C#
double value = 3.14159;

double rounded = Math.Round(value, 2);

Console.WriteLine(rounded); // 3.14

小数点第2位まで必ず表示したい場合は、ToString("F2")を使います。

C#
double value = 3.1;

Console.WriteLine(value.ToString("F2")); // 3.10

Math.Roundは数値の丸め、ToString("F2")は表示形式の指定です。

12-3. 2.5が3にならないのはなぜですか?

Math.Round(2.5)は、既定でMidpointRounding.ToEvenを使うためです。

C#
Console.WriteLine(Math.Round(2.5)); // 2

2.523のちょうど中間です。ToEvenでは偶数側に丸めるため、結果は2になります。

3にしたい場合は、MidpointRounding.AwayFromZeroを指定します。

C#
Console.WriteLine(Math.Round(2.5, MidpointRounding.AwayFromZero)); // 3

12-4. decimalとdoubleのどちらを使うべきですか?

金額計算や税計算など、10進数としての正確さが重要な場合はdecimalを使うのがおすすめです。

C#
decimal amount = 1.005m;
decimal rounded = Math.Round(amount, 2, MidpointRounding.AwayFromZero);

Console.WriteLine(rounded); // 1.01

科学計算、座標、統計処理など、広い範囲の実数を扱う場合はdoubleが適していることがあります。

C#
double x = 3.141592653589793;
double rounded = Math.Round(x, 4);

Console.WriteLine(rounded); // 3.1416

判断に迷う場合は、金額ならdecimal、一般的な実数計算ならdoubleと考えるとよいでしょう。

12-5. Math.RoundとToStringの違いは何ですか?

Math.Roundは、数値そのものを丸めます。

C#
double value = 1.2345;

double rounded = Math.Round(value, 2);

Console.WriteLine(rounded); // 1.23

ToStringは、数値を文字列として表示する形式を指定します。

C#
double value = 1.2;

string text = value.ToString("F2");

Console.WriteLine(text); // 1.20

計算結果として値を丸めたいならMath.Round、画面や帳票で見た目を整えたいならToStringを使います。

まとめ

C#のMath.Roundは、小数や金額、平均値、パーセンテージなどを丸めるときに便利なメソッドです。ただし、既定の丸め方式は一般的な四捨五入ではなく、MidpointRounding.ToEvenです。そのため、Math.Round(2.5)3ではなく2になります。

一般的な四捨五入をしたい場合は、次のようにMidpointRounding.AwayFromZeroを明示しましょう。

C#
Math.Round(value, digits, MidpointRounding.AwayFromZero);

また、金額計算ではdoubleではなくdecimalを使うことが重要です。

C#
decimal amount = 1234.565m;

decimal rounded = Math.Round(amount, 2, MidpointRounding.AwayFromZero);

表示桁数を揃えたいだけなら、Math.RoundではなくToString("F2")などの書式指定を使います。

C#
decimal amount = 1234.5m;

Console.WriteLine(amount.ToString("F2")); // 1234.50

Math.Roundを正しく使うポイントは、次の5つです。

ポイント内容
既定はToEven普通の四捨五入とは限らない
四捨五入したいならAwayFromZero丸め方式を明示する
金額はdecimaldoubleの丸め誤差を避ける
表示はToString数値の丸めと表示整形を分ける
境界値をテストする2.5-2.51.005などを確認する

Math.Roundはシンプルに見えますが、丸め方式や数値型を理解していないと、実務で思わぬ不具合につながります。特に金額計算や集計処理では、丸め方式、丸めるタイミング、使用する型を明確にして実装しましょう。