C#の四捨五入を完全解説|Math.Roundの使い方・桁数指定・切り捨てとの違い

はじめに

C#で数値を四捨五入するときは、基本的にMath.Roundメソッドを使用します。ただし、Math.Roundには初心者が間違えやすい重要な特徴があります。

特に注意したいのが、次の3点です。

  • デフォルトでは一般的な四捨五入ではなく「銀行丸め」が使われる

  • doubleでは浮動小数点数の誤差が発生することがある

  • 四捨五入した値と、画面に表示する桁数は別に考える必要がある

たとえば、Math.Round(2.5)の結果は3ではなく2です。これは不具合ではなく、デフォルトの丸め方法であるMidpointRounding.ToEvenの仕様によるものです。一般的な「5以上なら切り上げ」に近い処理を行いたい場合は、MidpointRounding.AwayFromZeroを明示的に指定します。Microsoft Learn+1

この記事では、C#の四捨五入について、基本構文から桁数指定、doubledecimalの違い、切り捨て・切り上げとの使い分けまで詳しく解説します。

1. C#で四捨五入する基本

1-1. C#の四捨五入はMath.Roundを使う

C#で四捨五入するときは、System.MathクラスのRoundメソッドを使用します。

C#
double result = Math.Round(12.6);

Console.WriteLine(result); // 13

Math.Roundは静的メソッドなので、Mathクラスのインスタンスを作成する必要はありません。

doubleを渡した場合はdoubledecimalを渡した場合はdecimalが返されます。整数型のintが自動的に返されるわけではない点に注意してください。Microsoft Learn

1-2. Math.Roundの基本構文

Math.Roundの代表的な構文は次のとおりです。

C#
Math.Round(value);
Math.Round(value, digits);
Math.Round(value, mode);
Math.Round(value, digits, mode);

それぞれの引数には、次の値を指定します。

  • value:四捨五入する数値

  • digits:結果として残す小数点以下の桁数

  • mode:端数がちょうど中間値だった場合の丸め方

一般的な四捨五入に近い処理を行う場合は、次の形式を使用します。

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

digitsに指定できる範囲は、doubleでは0~15、decimalでは0~28です。範囲外の値を指定するとArgumentOutOfRangeExceptionが発生します。Microsoft Learn+1

1-3. まず覚えるべき最小コード例

一般的な四捨五入を行いたい場合は、まず次のコードを覚えておくとよいでしょう。

C#
double value = 12.5;

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

Console.WriteLine(result); // 13

小数点以下2桁まで残す場合は、次のように記述します。

C#
decimal value = 12.345m;

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

Console.WriteLine(result); // 12.35

金額計算では、12.345mのように末尾へmを付けてdecimalリテラルとして扱うのが基本です。

1-4. int・double・decimalで戻り値はどう変わるか

Math.Roundの戻り値は、使用したオーバーロードによって変わります。

C#
double doubleValue = 12.6;
double doubleResult = Math.Round(doubleValue);

decimal decimalValue = 12.6m;
decimal decimalResult = Math.Round(decimalValue);

doubleを渡した場合、戻り値はdoubleです。

C#
double result = Math.Round(12.6);

decimalを渡した場合、戻り値はdecimalです。

C#
decimal result = Math.Round(12.6m);

一方、Math.Roundは整数型のintを直接返しません。整数として利用したい場合は、明示的に変換します。

C#
int result = checked(
(int)Math.Round(12.6, MidpointRounding.AwayFromZero)
);

もともとintである値には小数部分がないため、通常は四捨五入する必要がありません。

2. Math.Roundの使い方

2-1. 小数を整数に四捨五入する

小数を整数相当の値に四捨五入する場合は、Math.Roundへ数値だけを渡します。

C#
double value = 12.7;
double result = Math.Round(value);

Console.WriteLine(result); // 13

ただし、端数がちょうど.5の場合は銀行丸めになります。

C#
Console.WriteLine(Math.Round(12.5)); // 12
Console.WriteLine(Math.Round(13.5)); // 14

一般的な四捨五入を行いたい場合は、AwayFromZeroを指定します。

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

2-2. 小数点以下の桁数を指定して四捨五入する

小数点以下の桁数は、第2引数で指定します。

C#
double value = 12.3456;

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

Console.WriteLine(result); // 12.35

第2引数の2は、「小数点以下2桁を残す」という意味です。四捨五入の判定には、その1つ下の小数第3位が使われます。

C#
decimal value = 12.344m;

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

Console.WriteLine(result); // 12.34

2-3. 小数第1位・第2位・第3位で四捨五入する

「小数第何位で四捨五入するか」と「小数点以下何桁を残すか」は混同しやすいため注意が必要です。

小数第1位を見て、整数に丸める場合はdigits0を指定します。

C#
decimal value = 12.6m;

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

Console.WriteLine(result); // 13

小数第2位を見て、小数第1位まで残す場合は1です。

C#
decimal value = 12.36m;

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

Console.WriteLine(result); // 12.4

小数第3位を見て、小数第2位まで残す場合は2です。

C#
decimal value = 12.345m;

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

Console.WriteLine(result); // 12.35

整理すると、指定する数値は「四捨五入する桁」ではなく「結果に残したい小数点以下の桁数」です。

2-4. 戻り値をintに変換する場合の注意点

Math.Roundの結果をintへ変換するには、明示的なキャストを使用します。

C#
double value = 12.6;

int result = checked(
(int)Math.Round(value, MidpointRounding.AwayFromZero)
);

Console.WriteLine(result); // 13

checkedを付けると、結果がintの範囲を超えた場合にOverflowExceptionを発生させられます。

外部入力を扱う場合は、NaNや無限大も確認しましょう。

C#
static int RoundToInt(double value)
{
if (!double.IsFinite(value))
{
throw new ArgumentOutOfRangeException(
nameof(value),
"有限の数値を指定してください。"
);
}

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

if (rounded < int.MinValue || rounded > int.MaxValue)
{
throw new OverflowException(
"四捨五入後の値がintの範囲を超えています。"
);
}

return checked((int)rounded);
}

2-5. ToStringで表示桁数を整える方法

Math.Roundは数値を丸めるメソッドであり、表示桁数を固定するメソッドではありません。

C#
decimal value = 12.3m;
decimal rounded = Math.Round(value, 2);

Console.WriteLine(rounded); // 12.3

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

C#
decimal value = 12.3m;

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

Console.WriteLine(text); // 12.30

丸め規則も明示したい場合は、先にMath.Roundを実行してから表示形式を指定します。

C#
decimal value = 12.345m;

string text = Math.Round(
value,
2,
MidpointRounding.AwayFromZero
).ToString("F2");

Console.WriteLine(text); // 12.35

ToStringは文字列表現を作る処理であり、元の数値自体は変更しません。書式指定子の精度は表示する桁数を制御するもので、数値を永続的に丸める機能ではありません。Microsoft Learn

3. C#のMath.Roundで2.5が2になる理由

3-1. Math.Roundのデフォルトは「銀行丸め」

Math.Roundのデフォルトでは、MidpointRounding.ToEvenが使用されます。

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

この方式は、一般に「銀行丸め」「偶数丸め」「最近接偶数丸め」などと呼ばれます。

常に5を切り上げるのではなく、丸めた結果の末尾が偶数になる方向を選びます。大量の数値を繰り返し丸める際に、常に値が大きくなる方向へ偏ることを抑える目的があります。Microsoft Learn

3-2. ToEvenとは何か

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

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

Console.WriteLine(
Math.Round(3.5, MidpointRounding.ToEven)
); // 4

2.523の中間です。偶数である2が選ばれます。

3.534の中間です。偶数である4が選ばれます。

小数点以下1桁を残す場合も同じです。

C#
Console.WriteLine(
Math.Round(1.25m, 1, MidpointRounding.ToEven)
); // 1.2

Console.WriteLine(
Math.Round(1.35m, 1, MidpointRounding.ToEven)
); // 1.4

3-3. 一般的な四捨五入と銀行丸めの違い

一般的な四捨五入では、対象桁の次が5以上なら、絶対値が大きくなる方向へ丸める運用がよく使われます。

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

銀行丸めでは、結果が偶数になる方向へ丸めます。

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

違いが発生するのは、基本的に値が丸め候補のちょうど中間にある場合です。

C#
decimal value = 2.6m;

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

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

2.6は中間値ではないため、どちらも3になります。Microsoft Learn

3-4. 2.5・3.5・4.5の実行結果を比較

ToEvenAwayFromZeroの結果を比較すると、次のようになります。

ToEvenAwayFromZero
2.523
3.544
4.545
5.566
-2.5-2-3
-3.5-4-4

実際のコードは次のとおりです。

C#
decimal[] values =
{
2.5m,
3.5m,
4.5m,
5.5m,
-2.5m,
-3.5m
};

foreach (decimal value in values)
{
decimal toEven = Math.Round(
value,
MidpointRounding.ToEven
);

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

Console.WriteLine(
$"{value}: ToEven={toEven}, AwayFromZero={awayFromZero}"
);
}

3-5. 業務システムで注意すべきケース

業務システムでは、丸め方法を開発者の判断だけで決めてはいけません。

税額、請求額、ポイント、利率、割引額などは、業務仕様や契約、法令、外部システムとの連携仕様によって丸め方法が決められていることがあります。

次のように、丸め方式を省略したコードは意図が伝わりにくくなります。

C#
decimal amount = Math.Round(rawAmount);

業務上の規則が一般的な四捨五入なら、明示的に記述します。

C#
decimal amount = Math.Round(
rawAmount,
0,
MidpointRounding.AwayFromZero
);

銀行丸めが要件なら、ToEvenも省略せず明示すると保守しやすくなります。

C#
decimal amount = Math.Round(
rawAmount,
0,
MidpointRounding.ToEven
);

4. 一般的な四捨五入をする方法

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

一般的な「5以上なら絶対値が大きくなる方向へ丸める」処理には、MidpointRounding.AwayFromZeroを指定します。

C#
decimal value = 2.5m;

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

Console.WriteLine(result); // 3

AwayFromZeroは、値が中間にある場合、ゼロから離れる方向へ丸める方式です。正の2.53、負の-2.5-3になります。Microsoft Learn

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

整数に丸める場合は、値と丸め方法を指定します。

C#
double value = 8.5;

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

Console.WriteLine(result); // 9

decimalでも同様です。

C#
decimal value = 8.5m;

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

Console.WriteLine(result); // 9

戻り値は整数型ではなく、入力に対応したdoubleまたはdecimalです。

4-3. 桁数指定とAwayFromZeroを組み合わせる

小数点以下の桁数と丸め方法を同時に指定する場合は、3つの引数を渡します。

C#
decimal value = 123.455m;

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

Console.WriteLine(result); // 123.46

実務では、この形式を使うと丸めの意図が明確になります。

C#
decimal roundedPrice = Math.Round(
price,
2,
MidpointRounding.AwayFromZero
);

4-4. 負の数を四捨五入した場合の挙動

AwayFromZeroは、名前のとおりゼロから離れる方向へ丸めます。

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

「値が大きい方向」ではなく、「絶対値が大きくなる方向」である点が重要です。

C#
Console.WriteLine(
Math.Round(-2.4m, MidpointRounding.AwayFromZero)
); // -2

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

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

4-5. 四捨五入処理をメソッド化する実装例

同じ丸め処理を複数の場所で使用する場合は、メソッド化すると安全です。

C#
static decimal RoundHalfUp(decimal value, int digits)
{
return Math.Round(
value,
digits,
MidpointRounding.AwayFromZero
);
}

使用例は次のとおりです。

C#
decimal price = RoundHalfUp(123.455m, 2);

Console.WriteLine(price); // 123.46

引数の検証も追加できます。

C#
static decimal RoundHalfUp(decimal value, int digits)
{
if (digits is < 0 or > 28)
{
throw new ArgumentOutOfRangeException(
nameof(digits),
"decimalの桁数は0~28で指定してください。"
);
}

return Math.Round(
value,
digits,
MidpointRounding.AwayFromZero
);
}

5. 桁数指定の実践パターン

5-1. 小数点以下を四捨五入する

小数部分を四捨五入して整数相当にする場合は、digits0を指定します。

C#
decimal value = 123.6m;

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

Console.WriteLine(result); // 124

0の指定は省略できますが、業務コードでは残す桁数を明確にするため、あえて記述することもあります。

5-2. 小数第1位で四捨五入する

小数第1位を見て整数へ丸める場合は、結果として残す小数桁がないため0を指定します。

C#
decimal value = 18.5m;

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

Console.WriteLine(result); // 19

一方、「小数点以下1桁まで残す」という意味なら1を指定します。

C#
decimal value = 18.56m;

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

Console.WriteLine(result); // 18.6

5-3. 小数第2位で四捨五入する

小数第2位を見て、小数第1位まで残す場合は1を指定します。

C#
decimal value = 18.55m;

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

Console.WriteLine(result); // 18.6

小数点以下2桁を残したい場合は2です。

C#
decimal value = 18.555m;

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

Console.WriteLine(result); // 18.56

5-4. 整数の十の位・百の位で四捨五入する

Math.Rounddigitsには負の数を指定できません。そのため、十の位や百の位で丸める場合は、一度単位で割ってから丸め、再び掛けます。

十円単位に丸める例です。

C#
decimal value = 1234m;

decimal result = Math.Round(
value / 10m,
0,
MidpointRounding.AwayFromZero
) * 10m;

Console.WriteLine(result); // 1230

百円単位に丸める例です。

C#
decimal value = 1250m;

decimal result = Math.Round(
value / 100m,
0,
MidpointRounding.AwayFromZero
) * 100m;

Console.WriteLine(result); // 1300

汎用的なメソッドにすると便利です。

C#
static decimal RoundToUnit(decimal value, decimal unit)
{
if (unit <= 0)
{
throw new ArgumentOutOfRangeException(
nameof(unit),
"丸め単位には正の数を指定してください。"
);
}

return Math.Round(
value / unit,
0,
MidpointRounding.AwayFromZero
) * unit;
}
C#
Console.WriteLine(RoundToUnit(1234m, 10m));  // 1230
Console.WriteLine(RoundToUnit(1250m, 100m)); // 1300

5-5. 金額・税率・割合計算での使い分け

金額には、基本的にdecimalを使用します。

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

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

割合を小数点以下2桁まで表示する場合も、計算結果と表示形式を分けます。

C#
decimal rate = 12.345m;

decimal roundedRate = Math.Round(
rate,
2,
MidpointRounding.AwayFromZero
);

string display = roundedRate.ToString("F2");

税額や請求額では、四捨五入ではなく切り捨てが指定されることもあります。計算方法は必ず業務仕様に合わせてください。

6. doubleとdecimalの違いと四捨五入の注意点

6-1. doubleで誤差が発生する理由

doubleは、数値を2進数の浮動小数点形式で保持します。そのため、0.12.675のような10進小数を内部で正確に表現できない場合があります。

C#
double value = 0.1 + 0.2;

Console.WriteLine(value == 0.3); // Falseになる場合がある

表示上は同じ値に見えても、内部では想定よりわずかに小さい値や大きい値になっていることがあります。

Math.Roundが中間値を判定するときも、こうした表現誤差の影響を受けます。計算で作られた値が本来の.5よりわずかに小さければ、期待した方向に丸められない可能性があります。Microsoftの公式ドキュメントでも、浮動小数点数の表現と精度の問題によって、丸め結果が予想外になる場合があると説明されています。Microsoft Learn+1

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

decimalは10進数を基準にした高精度の数値型です。次のような処理に向いています。

  • 金額

  • 税額

  • 請求金額

  • 会計計算

  • 料金や単価

  • 10進表記の正確性が重要な割合

C#
decimal value = 2.675m;

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

Console.WriteLine(result); // 2.68

C#の公式ドキュメントでも、正確な10進精度が必要な金融計算などではdecimalの利用が推奨されています。Microsoft Learn

6-3. 金額計算ではdecimalを使う

金額をdoubleで扱うと、計算を繰り返すうちに誤差が蓄積する可能性があります。

C#
double price = 0.1;
double total = price * 10;

Console.WriteLine(total);

金額では、最初からdecimalを使用します。

C#
decimal price = 0.1m;
decimal total = price * 10m;

Console.WriteLine(total); // 1.0

リテラルの末尾へmを付けることも重要です。

C#
decimal taxRate = 0.1m;

mを付けない0.1doubleとして解釈されるため、そのままdecimal変数へ代入できません。

6-4. 期待通りに丸められないときの確認ポイント

四捨五入の結果が期待と異なる場合は、次の点を確認します。

第一に、丸め方法がToEvenになっていないか確認します。

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

一般的な四捨五入ならAwayFromZeroを指定します。

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

第二に、digitsへ指定した数値を確認します。

C#
Math.Round(12.345m, 2); // 小数点以下2桁を残す

第三に、元の型がdoubledecimalかを確認します。

C#
double doubleValue = 2.675;
decimal decimalValue = 2.675m;

第四に、元の値が計算途中ですでに誤差を含んでいないか確認します。

C#
Console.WriteLine(value.ToString("R"));

double"R"形式を利用すると、往復変換可能な精度で値を確認できます。

6-5. doubleからdecimalへ変換する際の注意点

doubleを後からdecimalへキャストしても、本来意図した10進数が完全に復元されるとは限りません。

C#
double source = 0.1;
decimal converted = (decimal)source;

この処理は、すでにdoubleとして近似された値をdecimalへ変換しています。

最初から10進数として扱いたい場合は、decimalリテラルを使用します。

C#
decimal value = 0.1m;

文字列や外部入力から金額を取得する場合も、直接decimalへ変換するほうが安全です。

C#
string input = "123.45";

if (!decimal.TryParse(input, out decimal amount))
{
Console.WriteLine("数値の形式が正しくありません。");
}

浮動小数点型同士の変換には明示的なキャストが必要な組み合わせがあり、変換しても各型の精度や表現方法の違いは残ります。Microsoft Learn

7. 四捨五入・切り捨て・切り上げの違い

7-1. 四捨五入はMath.Round

最も近い値へ丸める場合はMath.Roundを使用します。

C#
decimal value = 2.6m;

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

Console.WriteLine(result); // 3

中間値の処理を明確にするため、業務コードではMidpointRoundingを明示するのがおすすめです。

7-2. 切り捨てはMath.FloorとMath.Truncate

Math.Floorは、指定した数値以下で最大の整数へ丸めます。言い換えると、負の無限大方向への丸めです。

C#
Console.WriteLine(Math.Floor(2.9));  // 2
Console.WriteLine(Math.Floor(-2.9)); // -3

Math.Truncateは、小数部分を取り除き、ゼロ方向へ丸めます。

C#
Console.WriteLine(Math.Truncate(2.9));  // 2
Console.WriteLine(Math.Truncate(-2.9)); // -2

正の数では同じ結果になりますが、負の数では結果が異なります。Microsoft Learn+1

7-3. 切り上げはMath.Ceiling

Math.Ceilingは、指定した数値以上で最小の整数を返します。正の無限大方向への丸めです。

C#
Console.WriteLine(Math.Ceiling(2.1));  // 3
Console.WriteLine(Math.Ceiling(-2.1)); // -2

負の数に対しては、絶対値が小さくなる場合があります。

C#
double value = -2.1;
double result = Math.Ceiling(value);

Console.WriteLine(result); // -2

Math.Ceilingが返す型は、渡した値に応じてdoubleまたはdecimalであり、intではありません。Microsoft Learn

7-4. FloorとTruncateの違い

Math.FloorMath.Truncateの違いは、負の数を見ると分かりやすくなります。

C#
double value = -3.8;

Console.WriteLine(Math.Floor(value)); // -4
Console.WriteLine(Math.Truncate(value)); // -3

Floorは常に負の無限大方向へ移動します。

Truncateは小数部分を削除し、ゼロ方向へ移動します。

「小数部分を単純に取り除く」という意味の切り捨てであれば、Math.Truncateのほうが意図に合うことがあります。

7-5. 負の数で結果が変わるケース

正の数だけを確認していると、FloorTruncateCeilingの違いを見落としやすくなります。

C#
double value = -2.7;

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

Console.WriteLine(Math.Floor(value)); // -3
Console.WriteLine(Math.Ceiling(value)); // -2
Console.WriteLine(Math.Truncate(value)); // -2

業務仕様に「切り捨て」とだけ書かれている場合は、次のどちらを意味するのか確認が必要です。

  • 数学的に下方向へ丸める

  • 小数部分を取り除いてゼロへ近づける

7-6. Round・Floor・Ceiling・Truncateの比較表

メソッド丸める方向2.5-2.5
Math.Round(..., ToEven)最も近い値・中間は偶数2-2
Math.Round(..., AwayFromZero)最も近い値・中間はゼロから離す3-3
Math.Floor負の無限大方向2-3
Math.Ceiling正の無限大方向3-2
Math.Truncateゼロ方向2-2

丸め処理は、メソッド名だけでなく「どの方向へ丸めるか」で選ぶことが重要です。

8. よくある実装例

8-1. 消費税を四捨五入する

商品の税抜価格に税率を掛け、1円未満を四捨五入する例です。

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

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

Console.WriteLine(tax); // 199

税込価格まで計算する場合は次のようになります。

C#
decimal total = price + tax;

Console.WriteLine(total); // 2184

実際の税計算では、商品単位で丸めるのか、伝票全体で丸めるのかによって結果が変わります。

C#
decimal tax1 = Math.Round(
item1 * taxRate,
0,
MidpointRounding.AwayFromZero
);

decimal tax2 = Math.Round(
item2 * taxRate,
0,
MidpointRounding.AwayFromZero
);

個別に丸める方式と、合計してから丸める方式を混同しないようにしましょう。

8-2. 平均点を小数第1位で四捨五入する

平均点を小数点以下1桁まで残す例です。

C#
double[] scores = { 72, 81, 89, 94 };

double average = scores.Average();

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

Console.WriteLine(roundedAverage);

必ず小数点以下1桁を表示したい場合は、F1を使用します。

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

8-3. パーセント表示を小数第2位で四捨五入する

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

C#
decimal completed = 37m;
decimal total = 48m;

decimal percentage = total == 0
? 0
: completed / total * 100m;

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

Console.WriteLine($"{rounded:F2}%");

出力例は次のようになります。

77.08%

ゼロ除算を防ぐため、分母が0でないことを確認してください。

8-4. 価格を10円単位・100円単位で丸める

10円単位で丸める例です。

C#
decimal price = 1236m;

decimal roundedTo10 = Math.Round(
price / 10m,
0,
MidpointRounding.AwayFromZero
) * 10m;

Console.WriteLine(roundedTo10); // 1240

100円単位で丸める例です。

C#
decimal roundedTo100 = Math.Round(
price / 100m,
0,
MidpointRounding.AwayFromZero
) * 100m;

Console.WriteLine(roundedTo100); // 1200

共通メソッドを使用すると、単位を切り替えやすくなります。

C#
static decimal RoundPrice(decimal price, decimal unit)
{
if (unit <= 0)
{
throw new ArgumentOutOfRangeException(nameof(unit));
}

return Math.Round(
price / unit,
0,
MidpointRounding.AwayFromZero
) * unit;
}

8-5. LINQで集計結果を四捨五入する

LINQで金額を合計し、最後に四捨五入する例です。

C#
var orders = new[]
{
new { Amount = 100.125m },
new { Amount = 200.235m },
new { Amount = 300.345m }
};

decimal total = orders.Sum(order => order.Amount);

decimal roundedTotal = Math.Round(
total,
2,
MidpointRounding.AwayFromZero
);

Console.WriteLine(roundedTotal);

要素ごとに丸める場合は、次のように記述できます。

C#
decimal total = orders.Sum(order =>
Math.Round(
order.Amount,
2,
MidpointRounding.AwayFromZero
)
);

ただし、「各明細を丸めてから合計する処理」と「合計してから丸める処理」では、結果が異なることがあります。業務仕様に合った順序を選んでください。

9. C#の四捨五入でよくあるエラー・疑問

9-1. Math.Roundしても表示桁数が変わらない

Math.Roundで小数点以下2桁に丸めても、末尾のゼロが表示されるとは限りません。

C#
decimal value = 12.3m;
decimal rounded = Math.Round(value, 2);

Console.WriteLine(rounded); // 12.3

12.312.30は数値として同じです。必ず2桁表示したい場合は、数値書式を指定します。

C#
Console.WriteLine(rounded.ToString("F2")); // 12.30

文字列補間でも指定できます。

C#
Console.WriteLine($"{rounded:F2}"); // 12.30

9-2. 2.675が2.68にならない理由

double2.675は、内部で必ずしも正確な2.675として保持されるわけではありません。

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

実行環境や値が生成された計算過程によっては、丸め対象が内部的に2.675よりわずかに小さくなっていることがあります。その場合、小数第3位が正確な中間値として扱われず、期待した結果になりません。

金額など10進数の正確性が必要な値には、decimalを使用します。

C#
decimal value = 2.675m;

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

Console.WriteLine(result); // 2.68

doubleでは、リテラルだけでなく、加算や乗算を繰り返して作った値にも注意が必要です。公式ドキュメントでも、計算によって本来11.5になるはずの値が内部では11.499999999999998となり、期待どおりに丸められない例が示されています。Microsoft Learn

9-3. 四捨五入した結果を整数型にしたい

Math.Roundの結果をintへ変換します。

C#
double value = 15.6;

int result = checked(
(int)Math.Round(
value,
MidpointRounding.AwayFromZero
)
);

Console.WriteLine(result); // 16

値がintの範囲に収まることを保証できない場合は、事前に検証してください。

longが必要な場合も同様です。

C#
long result = checked(
(long)Math.Round(
value,
MidpointRounding.AwayFromZero
)
);

9-4. 小数点以下を必ず2桁表示したい

必ず2桁表示するには、F2書式を使用します。

C#
decimal value = 5m;

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

桁区切りも付ける場合はN2を使用します。

C#
decimal value = 12345.6m;

Console.WriteLine(value.ToString("N2")); // 12,345.60

通貨形式ならC2を使用できます。

C#
decimal value = 12345.6m;

Console.WriteLine(value.ToString("C2"));

通貨記号や桁区切り文字は、実行環境のカルチャー設定によって変わります。

9-5. .NETのバージョンで挙動は変わるのか

Math.Roundで丸め方法を省略した場合にMidpointRounding.ToEvenが使われる点は、現在の.NETでも基本です。

一方、数値の書式設定における中間値の扱いは、.NET Frameworkおよび.NET Core 2.0以前と、.NET Core 2.1以降で違いがあります。公式ドキュメントでは、前者はAwayFromZero、後者はToEvenに基づく書式設定になると説明されています。Microsoft Learn

また、現在のMidpointRoundingには次の値があります。

C#
MidpointRounding.ToEven
MidpointRounding.AwayFromZero
MidpointRounding.ToZero
MidpointRounding.ToNegativeInfinity
MidpointRounding.ToPositiveInfinity

ToZeroToNegativeInfinityToPositiveInfinityは、古い.NET Frameworkでは利用できない場合があります。対象フレームワークを確認して使用してください。Microsoft Learn+1

複数の実行環境で同じ結果を保証したい場合は、丸め方法と表示形式を明示し、自動テストで境界値を確認することが重要です。

10. C#の四捨五入に関するFAQ

10-1. C#で普通の四捨五入をするには?

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

C#
decimal result = Math.Round(
2.5m,
MidpointRounding.AwayFromZero
);

Console.WriteLine(result); // 3

小数点以下2桁まで残す場合は次のとおりです。

C#
decimal result = Math.Round(
12.345m,
2,
MidpointRounding.AwayFromZero
);

Console.WriteLine(result); // 12.35

10-2. Math.Roundの第2引数は何を指定する?

使用するオーバーロードによって意味が変わります。

第2引数が整数の場合は、結果として残す小数点以下の桁数です。

C#
Math.Round(12.345m, 2);

第2引数がMidpointRoundingの場合は、中間値の丸め方法です。

C#
Math.Round(
12.5m,
MidpointRounding.AwayFromZero
);

桁数と丸め方法を両方指定する場合は、3引数のオーバーロードを使用します。

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

10-3. MidpointRounding.AwayFromZeroとは?

値が2つの丸め候補のちょうど中間にある場合、ゼロから離れる方向へ丸める指定です。

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

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

日本で一般的にイメージされる四捨五入を実装するときによく使用されます。

10-4. ToEvenとAwayFromZeroはどちらを使うべき?

どちらを使うべきかは、業務仕様によって決まります。

ToEvenは、中間値を偶数方向へ丸めます。

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

AwayFromZeroは、中間値をゼロから離れる方向へ丸めます。

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

統計処理や大量の集計で丸めの偏りを抑えたい場合はToEven、一般的な四捨五入が要件ならAwayFromZeroが候補になります。

ただし、税額や請求金額などは独自の規則が定められていることがあるため、要件を確認したうえで選択してください。

10-5. 四捨五入と切り捨てはどう使い分ける?

最も近い数値へ丸めるならMath.Roundを使用します。

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

負の無限大方向へ丸めるならMath.Floorです。

C#
Math.Floor(-2.6m); // -3

小数部分を取り除いてゼロ方向へ丸めるならMath.Truncateです。

C#
Math.Truncate(-2.6m); // -2

正の無限大方向へ丸めるならMath.Ceilingを使用します。

C#
Math.Ceiling(-2.6m); // -2

特に負の数では結果が異なるため、「切り捨て」「切り上げ」という言葉だけで判断せず、丸める方向を明確にすることが重要です。

まとめ

C#で四捨五入する基本的な方法は、Math.Roundを使用することです。

ただし、Math.Roundのデフォルトは一般的な四捨五入ではなく、MidpointRounding.ToEvenによる銀行丸めです。

一般的な四捨五入に近い処理を実装する場合は、次のようにMidpointRounding.AwayFromZeroを明示します。

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

金額計算では、浮動小数点数の誤差を避けるためにdoubleではなくdecimalを使用するのが基本です。

また、数値の丸めと表示桁数の調整は別の処理です。小数点以下を必ず2桁表示する場合は、ToString("F2")や文字列補間の{value:F2}を使用します。

切り捨てや切り上げでは、目的に応じて次のメソッドを使い分けます。

  • 四捨五入:Math.Round

  • 負の無限大方向への丸め:Math.Floor

  • 正の無限大方向への丸め:Math.Ceiling

  • ゼロ方向への丸め:Math.Truncate

業務システムでは、丸め方法だけでなく、どのタイミングで丸めるかも結果に影響します。明細ごとに丸めるのか、合計後に丸めるのかを含め、仕様を明確にして実装しましょう。