C#のMath.Roundは四捨五入じゃない?小数点の丸め方・桁数指定・MidpointRoundingを徹底解説

はじめに

C#で小数を丸めるとき、多くの人が最初に使うのがMath.Roundです。しかし、Math.Round(2.5)の結果が3ではなく2になり、「C#のRoundは四捨五入ではないの?」と戸惑うケースは少なくありません。

結論から言うと、C#のMath.Roundは既定では一般的な四捨五入ではなく、MidpointRounding.ToEvenという丸め方式を使います。これは「最近接偶数への丸め」や「銀行丸め」と呼ばれる方式で、.5のようなちょうど中間の値を、近い偶数側へ丸めます。Microsoftの公式ドキュメントでも、Math.Round(Double)Math.Round(Decimal)などの既定の丸め規則はToEvenであると説明されています。

この記事では、C#のMath.Roundの基本、桁数指定、doubledecimalの違い、MidpointRounding.AwayFromZeroを使った一般的な四捨五入、切り上げ・切り捨てとの違いまで、実務で迷いやすいポイントをまとめて解説します。

1. C#のMath.Roundとは?まず押さえる丸め処理の基本

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

Math.Roundは、数値を指定した精度に丸めるためのメソッドです。整数に丸めることも、小数点以下の桁数を指定して丸めることもできます。

C#
double value = 3.14159;

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

たとえば、平均点を小数第1位まで表示したい場合、割合を小数第2位まで保存したい場合、金額を円単位に丸めたい場合などに使います。

1-2. Roundは「四捨五入」ではなく「丸め」を行うメソッド

日本語ではRoundを「四捨五入」と説明してしまうことがありますが、厳密にはRoundは「丸め」を行うメソッドです。

丸めには、次のように複数の方式があります。

  • 最も近い値へ丸める

  • ちょうど中間なら偶数へ丸める

  • ちょうど中間ならゼロから遠い方向へ丸める

  • 常に切り上げる

  • 常に切り捨てる

  • ゼロ方向へ丸める

C#のMath.Roundは、どの丸め方式を使うかによって結果が変わります。特に重要なのがMidpointRoundingです。

1-3. int・double・decimalで戻り値や挙動が変わるポイント

Math.Roundの主な対象はdoubledecimalです。doubleを渡した場合はdoubleが返り、decimalを渡した場合はdecimalが返ります。

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

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

intはすでに整数なので、通常は丸め処理の対象にする必要がありません。計算結果を整数にしたい場合は、Math.Roundの戻り値を必要に応じてintへ変換します。

C#
double average = 89.6;
int rounded = (int)Math.Round(average, MidpointRounding.AwayFromZero);

Console.WriteLine(rounded); // 90

ただし、doubleには2進浮動小数点による誤差があり、decimalは10進数の計算に向いています。そのため、金額計算ではdecimalを使うのが一般的です。

1-4. Math.Roundを使う代表的な場面

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

用途
平均値の丸め平均点を小数第1位まで表示する
割合の丸め達成率を小数第2位まで表示する
金額計算税額や単価を円単位に丸める
入力値の補正ユーザー入力を小数第2位まで保存する
レポート表示集計結果を見やすい桁数に整える

ただし、どの方式で丸めるかを曖昧にしたまま使うと、2.53.45のような境界値で想定外の結果になることがあります。

2. C#のMath.Roundが四捨五入にならない理由

2-1. Math.Round(2.5)が2になる理由

C#で次のコードを実行すると、結果は3ではなく2になります。

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

これはバグではありません。Math.Roundの既定の丸め方式が、一般的な四捨五入ではなくMidpointRounding.ToEvenだからです。

2.523のちょうど中間です。このときToEvenでは、近い偶数である2に丸めます。

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

1.512の中間なので偶数の2へ、2.523の中間なので偶数の2へ丸められます。

2-2. 既定の丸め方式はMidpointRounding.ToEven

Math.Round(value)Math.Round(value, digits)のように丸め方式を指定しない場合、C#では中間値を最も近い偶数へ丸めます。公式ドキュメントでも、Round(Double)Round(Decimal)Round(Double, Int32)Round(Decimal, Int32)の丸め規則はToEvenとされています。

つまり、次の2つは同じ意味です。

C#
Math.Round(2.5);
Math.Round(2.5, MidpointRounding.ToEven);

桁数を指定した場合も同様です。

C#
Math.Round(3.45, 1);
Math.Round(3.45, 1, MidpointRounding.ToEven);

2-3. ToEvenとは?最近接偶数への丸め・銀行丸めの考え方

MidpointRounding.ToEvenは、丸め先が2つある中間値の場合に、最後の桁が偶数になる方へ丸める方式です。Microsoftのドキュメントでは、ToEvenは「中間にある場合は最も近い偶数へ丸める」方式として定義されています。

たとえば、小数第1位に丸める場合は次のようになります。

C#
Console.WriteLine(Math.Round(3.45, 1)); // 3.4
Console.WriteLine(Math.Round(3.55, 1)); // 3.6

3.453.43.5の中間です。この場合、小数第1位の4が偶数なので3.4になります。

3.553.53.6の中間です。この場合、小数第1位の6が偶数なので3.6になります。

この方式は、常に.5を上げる方式に比べて、丸め誤差が一方向へ偏りにくいという特徴があります。そのため、金融や統計処理などで使われることがあります。公式ドキュメントでも、最近接偶数への丸めは金融・統計処理で標準的に使われる方式として説明されています。

2-4. 一般的な四捨五入との違いを表で比較

一般的な四捨五入に近い動きは、C#ではMidpointRounding.AwayFromZeroを指定して表現します。

Math.Round既定値 ToEvenAwayFromZero一般的な感覚
1.5222
2.5233
3.5444
4.5455
-1.5-2-2-2
-2.5-2-3-3

注意したいのは、AwayFromZeroは「ゼロから遠い方向」へ丸めるという点です。正の数では大きい方へ、負の数では小さい方、つまりよりマイナス側へ丸められます。

3. Math.Roundの基本的な使い方と構文

3-1. 小数を整数に丸める基本形

小数を整数に丸める基本形は次のとおりです。

C#
Math.Round(value)

例を見てみましょう。

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

.5以外の値は、最も近い整数へ丸められます。問題になりやすいのは、ちょうど中間になる.5のケースです。

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

小数点以下の桁数を指定するには、第二引数に桁数を指定します。

C#
Math.Round(value, digits)

たとえば、小数点以下2桁に丸める場合は次のように書きます。

C#
double value = 3.14159;

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

小数点以下1桁にする場合は1、3桁にする場合は3を指定します。

C#
Console.WriteLine(Math.Round(12.3456, 1)); // 12.3
Console.WriteLine(Math.Round(12.3456, 2)); // 12.35
Console.WriteLine(Math.Round(12.3456, 3)); // 12.346

3-3. Math.Round(value, digits)のdigitsの意味

digitsは「戻り値として残したい小数点以下の桁数」です。

指定意味
0整数に丸める123
1小数第1位まで残す123.5
2小数第2位まで残す123.46
3小数第3位まで残す123.457

たとえば、Math.Round(123.456, 2)は小数第2位まで残すため、確認する桁は小数第3位です。

C#
Console.WriteLine(Math.Round(123.456, 2)); // 123.46

double版ではdigitsは0〜15、decimal版ではdecimalsは0〜28の範囲で指定できます。範囲外を指定するとArgumentOutOfRangeExceptionが発生します。

3-4. double版とdecimal版の違い

doubleは2進浮動小数点数、decimalは10進数の表現に向いた数値型です。

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

Console.WriteLine(Math.Round(d, 2));
Console.WriteLine(Math.Round(m, 2));

doubleは高速で広い範囲の数値を扱えますが、10進小数を正確に表せないことがあります。そのため、0.10.152.675のような値で「想定と違う丸め結果」に見えることがあります。

一方、decimalは金額のような10進数ベースの計算に向いています。金額、税額、単価などを扱う場合は、まずdecimalを検討しましょう。

3-5. Math.Roundの主なオーバーロード一覧

Math.Roundには複数のオーバーロードがあります。代表的なものは次のとおりです。公式ドキュメントでも、Double版、Decimal版、桁数指定版、MidpointRounding指定版が用意されています。

メソッド説明
Math.Round(double value)doubleを整数に丸める
Math.Round(decimal d)decimalを整数に丸める
Math.Round(double value, int digits)doubleを指定桁数に丸める
Math.Round(decimal d, int decimals)decimalを指定桁数に丸める
Math.Round(double value, MidpointRounding mode)doubleを指定方式で整数に丸める
Math.Round(decimal d, MidpointRounding mode)decimalを指定方式で整数に丸める
Math.Round(double value, int digits, MidpointRounding mode)doubleを指定桁数・指定方式で丸める
Math.Round(decimal d, int decimals, MidpointRounding mode)decimalを指定桁数・指定方式で丸める

実務では、丸め方式を明示した次の形を使うと意図が伝わりやすくなります。

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

4. C#で一般的な四捨五入をしたい場合

4-1. MidpointRounding.AwayFromZeroを指定する

C#で一般的な四捨五入に近い結果を得たい場合は、MidpointRounding.AwayFromZeroを指定します。

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

AwayFromZeroは、中間値をゼロから離れる方向へ丸める方式です。Microsoftのドキュメントでも、AwayFromZeroは中間値をゼロから離れた最も近い数値へ丸める方式として定義されています。

4-2. Math.Round(value, digits, MidpointRounding.AwayFromZero)の使い方

小数点以下の桁数も指定して四捨五入したい場合は、次の形を使います。

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

例です。

C#
double value = 3.45;

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

結果は次のようになります。

3.4
3.5

既定のToEvenでは3.453.4になりますが、AwayFromZeroを指定すると3.5になります。

4-3. 小数点第1位・第2位・第3位で四捨五入するサンプル

「小数点第1位で四捨五入」という表現は、人によって意味がずれることがあります。C#のdigitsには「残したい小数点以下の桁数」を指定します。

C#
decimal value = 123.4565m;

// 小数第1位まで残す
Console.WriteLine(Math.Round(value, 1, MidpointRounding.AwayFromZero)); // 123.5

// 小数第2位まで残す
Console.WriteLine(Math.Round(value, 2, MidpointRounding.AwayFromZero)); // 123.46

// 小数第3位まで残す
Console.WriteLine(Math.Round(value, 3, MidpointRounding.AwayFromZero)); // 123.457

「小数第2位で四捨五入して小数第1位までにする」のか、「小数第3位で四捨五入して小数第2位までにする」のかを、仕様書では明確に書くことが重要です。

4-4. 負の数を四捨五入するときの注意点

AwayFromZeroは、名前のとおりゼロから遠い方向へ丸めます。そのため、負の数ではより小さい値、つまりマイナス方向へ進みます。

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

「負の数でも小数第1位が5なら絶対値を大きくする」のか、「常に上方向へ丸める」のかは、仕様としては別物です。

常に数直線上で上方向へ丸めたいならMath.Ceiling、常に下方向へ丸めたいならMath.Floorを使います。

4-5. 金額計算でAwayFromZeroを使うべきケース

金額計算でAwayFromZeroを使うべきなのは、仕様として「0.5円以上は1円にする」「小数第3位が5なら小数第2位を1増やす」のように決まっている場合です。

C#
decimal tax = 12.5m;
decimal roundedTax = Math.Round(tax, 0, MidpointRounding.AwayFromZero);

Console.WriteLine(roundedTax); // 13

ただし、会計・請求・税計算では、切り上げ、切り捨て、四捨五入、銀行丸めなどのルールが契約や法令、社内規程によって決まっていることがあります。AwayFromZeroを常に使えば正しいわけではありません。

重要なのは、丸め方式を明示し、仕様とコードを一致させることです。

5. MidpointRoundingの種類と使い分け

5-1. ToEven:既定の丸め方式

MidpointRounding.ToEvenは、Math.Roundの既定の丸め方式です。

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

中間値の場合、丸めた結果の末尾が偶数になる方を選びます。大量のデータを繰り返し丸める場合に、丸め誤差の偏りを抑えやすい方式です。

5-2. AwayFromZero:一般的な四捨五入に近い丸め

MidpointRounding.AwayFromZeroは、.5のような中間値をゼロから離れる方向へ丸めます。

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

日本語で一般的に言われる「5以上は上げる」に近い挙動が必要な場合に使います。

5-3. ToZero・ToNegativeInfinity・ToPositiveInfinityの違い

MidpointRoundingには、ToEvenAwayFromZero以外にも次の値があります。.NET Core 3.0以降では、ToZeroToNegativeInfinityToPositiveInfinityも使用できます。

種類意味
ToZeroゼロ方向へ丸める
ToNegativeInfinity負の無限大方向へ丸める
ToPositiveInfinity正の無限大方向へ丸める

MicrosoftのMidpointRoundingの説明でも、ToZeroは0方向、ToNegativeInfinityは下方向、ToPositiveInfinityは上方向への丸めとして定義されています。

C#
Console.WriteLine(Math.Round(3.47, 1, MidpointRounding.ToZero));             // 3.4
Console.WriteLine(Math.Round(3.47, 1, MidpointRounding.ToPositiveInfinity)); // 3.5
Console.WriteLine(Math.Round(3.47, 1, MidpointRounding.ToNegativeInfinity)); // 3.4

Console.WriteLine(Math.Round(-3.47, 1, MidpointRounding.ToZero)); // -3.4
Console.WriteLine(Math.Round(-3.47, 1, MidpointRounding.ToPositiveInfinity)); // -3.4
Console.WriteLine(Math.Round(-3.47, 1, MidpointRounding.ToNegativeInfinity)); // -3.5

5-4. 丸め方式ごとの実行結果比較

小数第1位に丸める例で比較してみます。

C#
decimal value = 3.45m;

Console.WriteLine(Math.Round(value, 1, MidpointRounding.ToEven));
Console.WriteLine(Math.Round(value, 1, MidpointRounding.AwayFromZero));
Console.WriteLine(Math.Round(value, 1, MidpointRounding.ToZero));
Console.WriteLine(Math.Round(value, 1, MidpointRounding.ToPositiveInfinity));
Console.WriteLine(Math.Round(value, 1, MidpointRounding.ToNegativeInfinity));

結果は次のようになります。

3.4
3.5
3.4
3.5
3.4

負の値では結果が変わります。

C#
decimal value = -3.45m;

Console.WriteLine(Math.Round(value, 1, MidpointRounding.ToEven));
Console.WriteLine(Math.Round(value, 1, MidpointRounding.AwayFromZero));
Console.WriteLine(Math.Round(value, 1, MidpointRounding.ToZero));
Console.WriteLine(Math.Round(value, 1, MidpointRounding.ToPositiveInfinity));
Console.WriteLine(Math.Round(value, 1, MidpointRounding.ToNegativeInfinity));
-3.4
-3.5
-3.4
-3.4
-3.5

負の数を扱う場合は、AwayFromZeroToPositiveInfinityToNegativeInfinityの違いを特に意識しましょう。

5-5. 業務システム・会計・統計処理での選び方

丸め方式は、処理の目的によって選びます。

目的候補
一般的な四捨五入に近い処理AwayFromZero
丸め誤差の偏りを抑えたい集計ToEven
小数部をゼロ方向へ落としたいToZero
常に上方向へ丸めたいToPositiveInfinityまたはMath.Ceiling
常に下方向へ丸めたいToNegativeInfinityまたはMath.Floor

業務システムでは、「どれが正しいか」よりも「仕様としてどれに決めるか」が重要です。画面、帳票、API、データベース保存で丸め方式がバラバラになると、不一致の原因になります。

6. Math.Roundで「結果がおかしい」と感じる原因

6-1. 0.5の扱いが想定と違う

最も多い原因は、.5の扱いです。

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

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

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

6-2. doubleの浮動小数点誤差によるズレ

doubleでは、10進小数を正確に表せない場合があります。そのため、見た目には2.675でも、内部的には少し小さい値として扱われ、丸め結果が想定と違うことがあります。

Microsoftの公式ドキュメントでも、浮動小数点値の精度損失により、Round(Double, Int32, MidpointRounding)が指定した中間値を丸めていないように見える場合があると説明されています。例として、2.1352.14ではなく2.13に丸められるケースが紹介されています。

C#
double value = 2.675;

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

このようなケースでは、doubleではなくdecimalを使うことで意図に近い結果を得られることがあります。

6-3. decimalを使うべきケース

次のような値を扱う場合は、decimalを優先して検討します。

  • 金額

  • 税額

  • 単価

  • 請求額

  • 会計上の割合

  • 人間が入力する10進小数

C#
decimal price = 123.455m;

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

Console.WriteLine(rounded); // 123.46

decimalリテラルには末尾にmを付けます。

C#
decimal value = 0.15m;

mを付けないと、doubleとして扱われる点に注意しましょう。

6-4. 0.15や2.675の丸め結果に注意

0.152.675のような値は、丸め処理の説明でよく出てくる注意ポイントです。

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

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

doubleでは、入力した10進小数と内部表現が完全に一致しないことがあります。金額のように「人間が見た10進小数どおりに丸めたい」場合は、decimalを使う方が安全です。

6-5. 表示上の桁数と数値の丸めは別物

Math.Roundは数値そのものを丸めます。一方、ToString("F2")などの書式指定は、表示する文字列を整える処理です。

C#
double value = 123.456;

double rounded = Math.Round(value, 2);
string text = value.ToString("F2");

Console.WriteLine(rounded); // 123.46
Console.WriteLine(text); // 123.46

表示結果が同じに見えても、意味は異なります。

C#
double value = 123.456;

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

Console.WriteLine(value); // 123.456
Console.WriteLine(text); // 123.46

ToString("F2")は表示用の文字列を作るだけで、元のvalueは変わりません。計算に使う値を丸めたいのか、画面表示だけ整えたいのかを分けて考えましょう。

7. 切り上げ・切り捨て・表示フォーマットとの違い

7-1. Math.Ceilingで切り上げる方法

Math.Ceilingは、指定した値以上の最小の整数へ丸めます。つまり、数直線上で正の方向へ進みます。

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

負の数では-3.9-3になる点に注意してください。

7-2. Math.Floorで切り捨てる方法

Math.Floorは、指定した値以下の最大の整数へ丸めます。つまり、数直線上で負の方向へ進みます。

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

「小数部を単純に削除する」とは異なります。負の数では、より小さい値になります。

7-3. Math.Truncateで小数部を削除する方法

Math.Truncateは、小数部を削除してゼロ方向へ丸めます。

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

正の数だけを見ているとFloorと似ていますが、負の数では結果が異なります。

7-4. ToStringや文字列フォーマットで小数点以下を表示する方法

表示だけ小数点以下2桁にしたい場合は、ToString("F2")や文字列補間の書式指定を使えます。

C#
double value = 123.4;

Console.WriteLine(value.ToString("F2")); // 123.40
Console.WriteLine($"{value:F2}"); // 123.40

F2は小数点以下2桁で表示する指定です。値を保存したり、後続の計算で使ったりする目的ならMath.Round、画面や帳票で見せるだけなら書式指定、というように使い分けます。

7-5. Round・Ceiling・Floor・Truncateの使い分け早見表

メソッド役割3.9-3.9
Math.Round最も近い値へ丸める4-4
Math.Ceiling正の方向へ切り上げる4-3
Math.Floor負の方向へ切り下げる3-4
Math.Truncate小数部を削除する3-3

「切り上げ」「切り捨て」という日本語だけでは、負の数で意味が曖昧になることがあります。仕様では「正の方向」「負の方向」「ゼロ方向」まで明記すると安全です。

8. Math.Roundの実用サンプル

8-1. 小数点以下2桁で丸める

C#
decimal value = 123.456m;

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

Console.WriteLine(result); // 123.46

小数点以下2桁で扱う値は、金額、割合、計測値などでよく登場します。一般的な四捨五入が必要なら、MidpointRounding.AwayFromZeroを明示しておくと意図が伝わりやすくなります。

8-2. 消費税や金額を丸める

金額計算ではdecimalを使い、丸め方式を仕様に合わせます。

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

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

Console.WriteLine(roundedTax);

ただし、税額計算では「切り捨て」「切り上げ」「四捨五入」のどれを使うかが業務ルールで決まっていることがあります。コードを書く前に、仕様を確認しましょう。

切り捨てが必要なら、次のような処理も考えられます。

C#
decimal roundedDown = Math.Floor(tax);

8-3. 平均値や割合を丸める

平均値を小数第1位まで表示する例です。

C#
double[] scores = { 80, 90, 75, 88 };

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

Console.WriteLine(roundedAverage);

統計処理や集計では、既定のToEvenが適している場合もあります。一方、ユーザーが一般的な四捨五入を期待する画面では、AwayFromZeroを明示した方が分かりやすいことがあります。

C#
double roundedAverage = Math.Round(average, 1, MidpointRounding.AwayFromZero);

8-4. パーセント表示用に丸める

割合をパーセント表示にする例です。

C#
decimal achieved = 37m;
decimal total = 80m;

decimal rate = achieved / total * 100;
decimal roundedRate = Math.Round(rate, 1, MidpointRounding.AwayFromZero);

Console.WriteLine($"{roundedRate}%"); // 46.3%

表示だけなら、次のように書式指定を使うこともできます。

C#
Console.WriteLine($"{rate:F1}%");

保存する数値を丸めるのか、表示だけ整えるのかで使い分けましょう。

8-5. ユーザー入力値を丸めて保存する

ユーザーが入力した数値を小数点以下2桁に丸めて保存する例です。

C#
string input = "123.456";

if (decimal.TryParse(input, out decimal value))
{
decimal normalized = Math.Round(value, 2, MidpointRounding.AwayFromZero);
Console.WriteLine(normalized); // 123.46
}
else
{
Console.WriteLine("数値を入力してください。");
}

入力値を丸めて保存する場合、保存前に丸めるのか、表示時だけ丸めるのかを決めておく必要があります。後から再計算する可能性があるなら、元の値を保持しておく設計も検討しましょう。

9. C#のMath.Roundで失敗しないためのベストプラクティス

9-1. 四捨五入したいならMidpointRoundingを明示する

Math.Round(value)Math.Round(value, digits)だけを書くと、既定のToEvenになります。一般的な四捨五入を期待しているなら、AwayFromZeroを明示しましょう。

C#
var result = Math.Round(value, 2, MidpointRounding.AwayFromZero);

コードを読む人にも「この処理は一般的な四捨五入を意図している」と伝わります。

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

金額計算では、doubleではなくdecimalを優先します。

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

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

doubleは科学技術計算や座標計算などには便利ですが、10進数を正確に扱いたい金額計算では誤差が問題になりやすいです。

9-3. 表示用の丸めと計算用の丸めを分ける

表示だけを整えたい場合は、ToString("F2")や文字列補間を使います。

C#
Console.WriteLine($"{value:F2}");

計算に使う値自体を丸めたい場合は、Math.Roundを使います。

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

表示用の丸めを計算に使ってしまうと、後続処理で誤差や不一致が出ることがあります。

9-4. 丸め仕様をプロジェクト内で統一する

丸め処理は、画面、API、バッチ、帳票、データベース保存など、さまざまな場所に出てきます。各所で別々にMath.Roundを書くと、丸め方式がばらつく原因になります。

共通メソッドを用意しておくと、安全です。

C#
public static class RoundingHelper
{
public static decimal RoundMoney(decimal value)
{
return Math.Round(value, 0, MidpointRounding.AwayFromZero);
}

public static decimal RoundRate(decimal value)
{
return Math.Round(value, 2, MidpointRounding.AwayFromZero);
}
}

丸め方式を1か所に集約すれば、仕様変更にも対応しやすくなります。

9-5. テストケースに0.5・負の数・境界値を含める

丸め処理のテストでは、通常の値だけでなく境界値を必ず含めましょう。

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

小数点以下の桁数指定でも、.005.015のような境界値を確認します。

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

特に金額や請求処理では、境界値テストが重要です。

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

10-1. Math.Round(1.5)とMath.Round(2.5)の結果が違うのはなぜ?

既定の丸め方式がMidpointRounding.ToEvenだからです。

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

1.512の中間なので偶数の2へ、2.523の中間なので偶数の2へ丸められます。

一般的な四捨五入に近い結果が必要なら、次のように書きます。

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

10-2. Math.Roundで必ず切り上げることはできる?

Math.Roundは「最も近い値へ丸める」ためのメソッドです。必ず切り上げたい場合は、通常はMath.Ceilingを使います。

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

小数点以下2桁で切り上げたい場合は、倍率をかけてからCeilingし、戻します。

C#
decimal value = 12.341m;

decimal result = Math.Ceiling(value * 100) / 100;

Console.WriteLine(result); // 12.35

10-3. 小数点第2位で四捨五入するには?

「小数点第2位で四捨五入」という表現が「小数第2位まで残す」という意味なら、digits2を指定します。

C#
decimal value = 12.345m;

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

Console.WriteLine(result); // 12.35

「小数第2位を見て小数第1位まで残す」という意味なら、digits1です。

C#
decimal value = 12.35m;

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

Console.WriteLine(result); // 12.4

仕様書では「小数第何位まで残す」と書くと誤解を減らせます。

10-4. doubleとdecimalのどちらを使うべき?

用途によって使い分けます。

向いている用途
double科学技術計算、座標、計測値、大量の数値計算
decimal金額、税額、単価、10進小数を正確に扱いたい処理

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

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

10-5. Math.RoundとToString("F2")は何が違う?

Math.Roundは数値を丸めます。ToString("F2")は表示用の文字列を作ります。

C#
double value = 12.3456;

double rounded = Math.Round(value, 2);
string formatted = value.ToString("F2");

Console.WriteLine(rounded); // 12.35
Console.WriteLine(formatted); // 12.35

見た目は同じでも、formattedは文字列です。元のvalueは変わりません。

C#
Console.WriteLine(value); // 12.3456

計算に使う値を丸めたいならMath.Round、表示だけ整えたいならToString("F2")$"{value:F2}"を使いましょう。

まとめ

C#のMath.Roundは、単純な四捨五入メソッドではなく、丸め方式に従って数値を丸めるメソッドです。特に重要なのは、丸め方式を指定しない場合、既定でMidpointRounding.ToEvenが使われる点です。

そのため、次のような結果になります。

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

一般的な四捨五入に近い結果が必要な場合は、MidpointRounding.AwayFromZeroを明示します。

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

また、金額計算ではdoubleではなくdecimalを使い、表示用の丸めと計算用の丸めを分けることが大切です。

C#でround処理を書くときは、次の3点を意識しましょう。

  1. 四捨五入したいならMidpointRounding.AwayFromZeroを明示する

  2. 金額や税額ではdecimalを優先する

  3. RoundCeilingFloorTruncateToStringの違いを理解して使い分ける

丸め処理は小さな違いに見えて、会計、請求、統計、画面表示では大きな不具合につながることがあります。Math.Roundの既定動作を理解し、プロジェクト内で丸め仕様を統一しておくことが、C#で失敗しない丸め処理の基本です。