C#のMath.Roundの使い方|四捨五入・切り捨てとの違いと小数点処理の注意点

はじめに

C#で小数を丸めたいときによく使うのがMath.Roundです。平均値、割合、金額、消費税、画面表示用の小数点処理など、実務でも登場頻度の高いメソッドです。

ただし、Math.Roundは単純な「四捨五入」と同じではありません。C#のMath.Roundは、何も指定しない場合、中間値を最も近い偶数に丸める「銀行丸め」と呼ばれる動作をします。Microsoftの公式ドキュメントでも、Round(Double)Round(Decimal)は中間値を最も近い偶数へ丸めると説明されています。

そのため、Math.Round(2.5)3ではなく2になるなど、初心者がつまずきやすいポイントがあります。この記事では、C#のMath.Roundの基本的な使い方から、四捨五入・切り捨て・切り上げとの違い、doubledecimalの注意点、実務で使えるサンプルコードまで解説します。

1. C#のMath.Roundとは?小数を丸める基本メソッド

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

Math.Roundは、数値を指定した桁数に丸めるためのメソッドです。たとえば、3.141593にしたり、3.14にしたりできます。

C#
double value = 3.14159;

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

主な用途は次のとおりです。

用途
整数に丸める3.64
小数点以下の桁数を指定する3.141593.14
金額計算で端数処理する税込価格消費税額を丸める
表示用に桁数を整える12.3456%12.35%

Math.Roundは「近い値に丸める」メソッドであり、必ずしも「小数点以下を捨てる」処理ではありません。切り捨てにはMath.FloorMath.Truncate、切り上げにはMath.Ceilingを使います。

1-2. Math.Roundの基本構文と戻り値

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

C#
Math.Round();
Math.Round(, 小数点以下の桁数);
Math.Round(, 丸め方法);
Math.Round(, 小数点以下の桁数, 丸め方法);

戻り値は、引数の型に応じてdoubleまたはdecimalになります。Microsoftの公式ドキュメントでは、double向け、decimal向け、丸め方法を指定するオーバーロードなどが用意されていることが示されています。

C#
double d = Math.Round(3.14);      // double
decimal m = Math.Round(3.14m); // decimal

1-3. int・double・decimalで使う場合の違い

Math.Roundでよく使うのはdoubledecimalです。

doubleは広い範囲の数値を扱える一方、2進数の浮動小数点として値を保持するため、0.1のような10進小数を正確に表せない場合があります。MicrosoftのC#リファレンスでも、0.1decimalでは正確に表現できますが、doublefloatでは正確に表現できず、予期しない丸め誤差が起きる可能性があると説明されています。

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

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

金額や税率など、10進数としての正確さが重要な値にはdecimalを使うのが基本です。一方、座標、統計、科学計算などで広い範囲や処理速度を重視する場合はdoubleが使われます。

intは整数型なので、小数部分がありません。すでに整数の値を丸める必要は基本的にありません。計算結果がdoubledecimalになったタイミングでMath.Roundを使います。

C#
int total = 10;
int count = 3;

double average = (double)total / count;
double rounded = Math.Round(average, 2);

Console.WriteLine(rounded); // 3.33

1-4. Mathクラスを使うために必要な前提知識

Math.RoundSystem.Mathクラスの静的メソッドです。通常のC#プロジェクトではusing System;を使うか、近年のプロジェクトテンプレートのように暗黙的なusingが有効であれば、そのままMath.Roundと書けます。

C#
using System;

class Program
{
static void Main()
{
double result = Math.Round(3.5);
Console.WriteLine(result);
}
}

静的メソッドなので、new Math()のようにインスタンスを作成する必要はありません。

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

2-1. 小数を最も近い整数に丸める

小数を最も近い整数に丸めるには、Math.Round(値)を使います。

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

このように、通常の小数であれば「近い整数」に丸められます。ただし、.5のようにちょうど中間の値は注意が必要です。既定ではMidpointRounding.ToEven、つまり最も近い偶数への丸めが使われます。

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

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

C#
double value = 123.4567;

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

Math.Round(value, 2)は「小数点以下2桁に丸める」という意味です。2を指定した場合、小数第3位を見て小数第2位までの値に丸められます。

doubledigitsには0から15まで、decimaldecimalsには0から28までの範囲を指定できます。範囲外の値を指定するとArgumentOutOfRangeExceptionの対象になります。

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

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

C#
double value = 12.3456;

double rounded1 = Math.Round(value, 1);
double rounded2 = Math.Round(value, 2);

Console.WriteLine(rounded1); // 12.3
Console.WriteLine(rounded2); // 12.35

注意したいのは、「小数点第1位で丸める」という表現が、人によって「小数第1位を残す」意味なのか、「小数第1位を見て整数にする」意味なのか曖昧になりやすい点です。

C#のMath.Round(value, digits)では、digitsは「戻り値に残す小数点以下の桁数」です。

C#
Math.Round(12.3456, 0); // 整数に丸める
Math.Round(12.3456, 1); // 小数点以下1桁まで残す
Math.Round(12.3456, 2); // 小数点以下2桁まで残す

2-4. decimal型で金額や精度が必要な値を丸める

金額計算ではdecimalを使うのが基本です。C#のdecimalは、通貨金額や利率のように小数点以下の桁数が重要な値に適していると公式ドキュメントで説明されています。

C#
decimal price = 1234.567m;

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

Console.WriteLine(rounded); // 1234.57

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

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

Console.WriteLine(tax); // 100

doubleで金額を扱うと、内部表現の誤差によって期待と違う丸め結果になることがあります。会計、請求、税計算などでは、仕様としてdecimalを選ぶことが重要です。

3. Math.Roundは単純な四捨五入ではない

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

C#のMath.Roundで最も注意すべき点は、既定の丸め方法が一般的な四捨五入ではないことです。

何も指定しない場合、Math.RoundMidpointRounding.ToEvenを使います。これは「中間値を最も近い偶数に丸める」方式です。MicrosoftのMidpointRoundingの説明でも、ToEvenは中間値を最も近い偶数に丸める戦略として定義されています。

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

一般的な感覚では、2.53になりそうですが、Math.Round(2.5)2になります。これはバグではなく、C#の既定動作です。

3-2. 1.5と2.5で結果が変わる理由

1.512のちょうど中間です。このとき偶数は2なので、Math.Round(1.5)2になります。

一方、2.523のちょうど中間です。このとき偶数は2なので、Math.Round(2.5)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

つまり、.5なら必ず大きい方に丸めるわけではありません。中間値の場合だけ、偶数側へ丸めるのが既定のルールです。

3-3. MidpointRounding.ToEvenとは

MidpointRounding.ToEvenは、丸め対象の値が2つの候補のちょうど中間にある場合、結果の最後の桁が偶数になる方を選ぶ丸め方法です。MidpointRoundingにはToEvenAwayFromZeroToZeroToNegativeInfinityToPositiveInfinityなどの値が定義されています。

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

ToEvenは、大量のデータを丸めたときに、常に上方向へ偏ることを避ける目的で使われます。そのため、統計処理や集計処理では合理的な選択になる場合があります。

3-4. 想定した四捨五入にならない代表例

想定と違う結果になりやすい例を見てみましょう。

C#
Console.WriteLine(Math.Round(2.5));    // 2
Console.WriteLine(Math.Round(12.5)); // 12
Console.WriteLine(Math.Round(3.45, 1)); // 3.4
Console.WriteLine(Math.Round(4.35, 1)); // 4.4

3.45を小数点以下1桁に丸める場合、候補は3.43.5です。3.4の小数第1位は4で偶数なので、3.4になります。

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

この挙動を知らないと、「C#の四捨五入がおかしい」「切り捨てられている」と感じる原因になります。

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

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

一般的な「5以上なら上げる」四捨五入に近い処理をしたい場合は、MidpointRounding.AwayFromZeroを指定します。

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

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

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

小数点以下の桁数と丸め方法を同時に指定するには、次の形式を使います。

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

例は次のとおりです。

C#
double value = 3.45;

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

Console.WriteLine(result); // 3.5

Math.Round(value, digits)だけだと既定のToEvenが使われます。業務仕様で「四捨五入」と明記されている場合は、AwayFromZeroを指定するかどうかを確認しましょう。

4-3. 小数点以下を指定して四捨五入するコード例

小数点以下2桁に一般的な四捨五入をしたい場合は、次のように書きます。

C#
decimal value = 123.455m;

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

Console.WriteLine(result); // 123.46

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

C#
double value = 123.455;

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

Console.WriteLine(result);

ただし、金額のように10進小数の正確性が必要な場面ではdecimalを使いましょう。doubleは2進浮動小数点のため、10進小数を完全に表せないことがあり、丸め結果が直感と異なる場合があります。

4-4. 負の数を四捨五入するときの挙動

AwayFromZeroは「0から遠ざかる方向」に丸めます。正の数では大きい方向、負の数では小さい方向になります。

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( 3.5, MidpointRounding.ToEven)); // 4
Console.WriteLine(Math.Round(-3.5, MidpointRounding.ToEven)); // -4

負の数を扱う場合、「四捨五入」と「0から遠ざける丸め」が業務仕様に合っているかを確認することが大切です。

5. Math.Roundと切り捨て・切り上げの違い

5-1. Math.Round・Math.Floor・Math.Ceiling・Math.Truncateの違い

C#には、丸め処理に使えるメソッドが複数あります。

メソッド意味
Math.Round最も近い値に丸める3.64
Math.Floor負の無限大方向に丸める3.93, -3.1-4
Math.Ceiling正の無限大方向に丸める3.14, -3.9-3
Math.Truncate0方向に丸める3.93, -3.9-3

Math.Floorは負の無限大方向、Math.Ceilingは正の無限大方向、Math.Truncateは0方向に丸める動作です。Microsoftの公式ドキュメントでも、Floorは負の無限大方向、Ceilingは正の無限大方向、Truncateは0方向に丸めると説明されています。

C#
double value = -3.7;

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

5-2. 切り捨てにはMath.FloorとMath.Truncateのどちらを使うべきか

正の数だけを扱う場合、Math.FloorMath.Truncateは同じ結果になることが多いです。

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

しかし、負の数では結果が変わります。

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

「小数部分を単純に削除したい」ならMath.Truncate、「必ず指定値以下の最大整数にしたい」ならMath.Floorを使います。

5-3. 負の数でFloorとTruncateの結果が変わる理由

Math.Floorは負の無限大方向に丸めます。つまり、数直線上で左側に向かって丸めます。

C#
Math.Floor(3.9)   // 3
Math.Floor(-3.9) // -4

一方、Math.Truncateは0方向に丸めます。小数部分を取り除くイメージです。

C#
Math.Truncate(3.9)   // 3
Math.Truncate(-3.9) // -3

負の数では「より小さい値」と「0に近い値」が一致しないため、FloorTruncateで結果が変わります。

5-4. 用途別に使い分ける丸め処理の選び方

用途ごとの使い分けは次のとおりです。

用途使うメソッド
最も近い値に丸めたいMath.Round
一般的な四捨五入にしたいMath.Round(value, digits, MidpointRounding.AwayFromZero)
常に小さい方向へ丸めたいMath.Floor
常に大きい方向へ丸めたいMath.Ceiling
小数部分を削除したいMath.Truncate
表示だけ桁数を整えたいToStringや文字列フォーマット

業務では「四捨五入」「切り捨て」「切り上げ」という言葉だけでなく、負の数や中間値をどう扱うかまで決めておくと、実装ミスを防げます。

6. 小数点処理で注意すべきポイント

6-1. double型の誤差で期待通りに丸められないケース

doubleは2進浮動小数点で値を表すため、10進小数を正確に表現できないことがあります。MicrosoftのDoubleのドキュメントでも、2進小数では一部の小数値を正確に表せず、浮動小数点値は実数を近似するものだと説明されています。

C#
double value = 0.1 + 0.2;

Console.WriteLine(value); // 0.30000000000000004 など
Console.WriteLine(Math.Round(value, 2)); // 0.3

表示上は問題がないように見えても、内部ではわずかな誤差を持っていることがあります。その誤差が、.5付近の丸め結果に影響する場合があります。

6-2. 金額計算ではdecimal型を使うべき理由

金額計算では、doubleではなくdecimalを使うのが一般的です。decimalは小数点以下の桁数が重要な値、たとえば通貨金額や利率に適していると説明されています。

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

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

Console.WriteLine(tax); // 198

decimalを使うことで、10進小数としての扱いが安定しやすくなります。ただし、decimalでも丸め方法を指定しなければ既定はToEvenです。金額計算では、法律・会計ルール・社内仕様に合わせてAwayFromZero、切り捨て、切り上げなどを明確に選びましょう。

6-3. 1.005を小数点第2位で丸めるときの注意点

1.005を小数点以下2桁に丸める処理は、丸めの注意点を理解するうえでよく使われる例です。

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

1.005mdecimalです。小数点以下2桁に丸める場合、1.001.01の中間値になります。既定のToEvenでは、偶数側の1.00が選ばれることがあります。

一般的な四捨五入として1.01にしたいなら、次のように書きます。

C#
decimal value = 1.005m;

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

Console.WriteLine(result); // 1.01

double1.005を扱うと、そもそも内部表現が厳密な1.005ではない可能性があります。金額や請求金額のような処理では、1.005mのようにdecimalリテラルを使いましょう。

6-4. 表示上の丸めと計算上の丸めを混同しない

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"

計算結果として丸めた値を使いたいならMath.Round、画面やログに表示するだけならToStringや文字列フォーマットを使います。

C#
double value = 12.3456;

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

表示だけを整えたい場面でMath.Roundを多用すると、後続の計算で丸め誤差が積み重なることがあります。計算内部では必要な精度を保ち、最後の表示時だけフォーマットする設計も検討しましょう。

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

7-1. Math.Round(double)の使い方

Math.Round(double)は、double値を最も近い整数に丸めます。中間値は既定で偶数側に丸められます。

C#
double value = 3.6;

double result = Math.Round(value);

Console.WriteLine(result); // 4

.5の値ではToEvenが使われます。

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

7-2. Math.Round(double, int)の使い方

Math.Round(double, int)は、double値を指定した小数点以下の桁数に丸めます。中間値は既定でToEvenです。

C#
double value = 3.456;

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

Console.WriteLine(result); // 3.46

第2引数のintは、残したい小数点以下の桁数です。

C#
Math.Round(3.456, 0); // 3
Math.Round(3.456, 1); // 3.5
Math.Round(3.456, 2); // 3.46

7-3. Math.Round(double, MidpointRounding)の使い方

Math.Round(double, MidpointRounding)は、整数に丸めるときの中間値の扱いを指定できます。

C#
double value = 2.5;

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

整数への丸めで、一般的な四捨五入に近い動作をさせたい場合に使います。

7-4. Math.Round(double, int, MidpointRounding)の使い方

Math.Round(double, int, MidpointRounding)は、小数点以下の桁数と丸め方法を同時に指定できる実務でよく使う形式です。

C#
double value = 3.45;

double result1 = Math.Round(value, 1, MidpointRounding.ToEven);
double result2 = Math.Round(value, 1, MidpointRounding.AwayFromZero);

Console.WriteLine(result1); // 3.4
Console.WriteLine(result2); // 3.5

仕様として「小数点以下2桁で四捨五入」と決まっているなら、次のように明示します。

C#
double value = 12.345;

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

Console.WriteLine(result);

7-5. decimal型で使えるMath.Roundのオーバーロード

decimalでも、doubleと同じようにMath.Roundを使えます。

C#
decimal value = 123.455m;

Console.WriteLine(Math.Round(value)); // 123
Console.WriteLine(Math.Round(value, 2)); // 123.46 または中間値の規則に従う
Console.WriteLine(Math.Round(value, MidpointRounding.AwayFromZero)); // 123
Console.WriteLine(Math.Round(value, 2, MidpointRounding.AwayFromZero)); // 123.46

decimal向けのdecimals引数は0から28まで指定できます。範囲外の場合はArgumentOutOfRangeExceptionの対象になります。

金額、税率、請求額、割引率などでは、decimalMidpointRoundingを組み合わせて、仕様に合った丸め処理を明示するのがおすすめです。

8. 実務でよく使うMath.Roundのサンプルコード

8-1. 消費税計算で小数を丸める

消費税計算では、端数処理の仕様を明確にする必要があります。ここでは例として、税額を小数点以下0桁に四捨五入します。

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

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

Console.WriteLine($"税抜価格: {price}");
Console.WriteLine($"消費税: {tax}");
Console.WriteLine($"税込価格: {total}");

切り捨てにする場合は、Math.RoundではなくMath.Floorなどを使います。

C#
decimal tax = Math.Floor(price * taxRate);

ただし、負の値を扱う可能性がある場合は、FloorTruncateの違いにも注意してください。

8-2. 平均値や割合を小数点第2位まで丸める

平均値を小数点以下2桁に丸める例です。

C#
int[] scores = { 80, 75, 92, 68, 88 };

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

Console.WriteLine(roundedAverage);

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

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

統計用途ではToEvenが適している場合もあります。業務仕様やレポートのルールに合わせて選びましょう。

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

割合をパーセント表示する場合、計算値に100を掛けてから丸めることがあります。

C#
double success = 87;
double total = 123;

double rate = success / total * 100;
double roundedRate = Math.Round(rate, 2, MidpointRounding.AwayFromZero);

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

表示だけでよければ、文字列フォーマットを使う方法もあります。

C#
double rate = success / total;

Console.WriteLine($"{rate:P2}");

P2はパーセント形式で小数点以下2桁に整形する指定です。計算結果を丸めて保持したいのか、表示だけ整えたいのかで使い分けましょう。

8-4. 画面表示だけ小数点以下を整える

画面表示だけ小数点以下2桁にしたい場合は、Math.Roundではなくフォーマットで十分なことがあります。

C#
double value = 123.4567;

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

計算用の値はそのまま保持し、表示時だけ整形することで、途中計算で不要な丸めが発生するのを防げます。

C#
double price = 123.4567;

// 計算用
double calculated = price * 1.1;

// 表示用
Console.WriteLine($"{calculated:F2}");

9. Math.Roundでよくあるエラー・疑問

9-1. 「四捨五入したのに切り捨てられる」原因

よくある原因は、Math.Roundの既定がToEvenであることです。

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

3.45を小数点以下1桁に丸める場合、3.43.5の中間です。既定では偶数側の3.4になります。

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

C#
Console.WriteLine(Math.Round(3.45, 1, MidpointRounding.AwayFromZero)); // 3.5

9-2. 小数点以下の桁数指定が反映されない原因

Math.Round(value, 2)は、小数点以下2桁まで丸めた数値を返します。しかし、戻り値が数値型である以上、末尾の0は表示されないことがあります。

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

Console.WriteLine(rounded); // 1.2

1.20と表示したい場合は、数値を丸めるのではなく表示形式を指定します。

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

Math.Roundは表示桁数を固定するメソッドではありません。表示の桁数をそろえたい場合は、ToString("F2")や文字列補間を使いましょう。

9-3. AwayFromZeroを指定しても期待通りにならない原因

MidpointRounding.AwayFromZeroを指定しても期待通りにならない場合、原因として多いのはdoubleの誤差です。

C#
double value = 1.005;

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

Console.WriteLine(result);

doubleでは1.005が内部的に厳密な1.005として保持されているとは限りません。金額などの10進小数を正確に扱いたい場合は、decimalを使います。

C#
decimal value = 1.005m;

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

Console.WriteLine(result); // 1.01

AwayFromZeroは「中間値だった場合の扱い」を指定するものです。そもそも値が内部的に中間値ではない場合、期待した結果と異なることがあります。

9-4. ToStringや文字列フォーマットとの違い

Math.RoundToStringは目的が違います。

C#
double value = 12.3456;

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

Console.WriteLine(rounded); // 数値
Console.WriteLine(text); // 文字列

Math.Roundは数値を丸めた結果を返します。後続の計算に使う場合はこちらです。

ToString("F2")$"{value:F2}"は、表示用の文字列を作ります。画面表示、CSV出力、ログ出力などで桁数を整える場合はこちらが適しています。

C#
decimal amount = 1234.5m;

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

「計算上の値を丸めたい」のか、「見た目だけ小数点以下2桁にしたい」のかを分けて考えることが重要です。

まとめ

C#のMath.Roundは、小数を丸めるための便利なメソッドです。整数に丸めるだけでなく、小数点以下の桁数を指定して丸めることもできます。

ただし、Math.Roundの既定動作は一般的な四捨五入ではなく、MidpointRounding.ToEvenによる銀行丸めです。.5のような中間値では、最も近い偶数側に丸められます。そのため、Math.Round(2.5)3ではなく2になります。

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

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

また、切り捨て・切り上げにはそれぞれ別のメソッドを使います。

C#
Math.Floor(value);     // 負の無限大方向
Math.Ceiling(value); // 正の無限大方向
Math.Truncate(value); // 0方向

金額や税計算では、doubleではなくdecimalを使うことも重要です。doubleは2進浮動小数点の誤差により、期待と異なる丸め結果になる場合があります。

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

ポイント内容
既定は銀行丸めToEvenで偶数側に丸められる
一般的な四捨五入は明示するAwayFromZeroを指定する
金額はdecimalを使うdoubleの誤差を避ける

丸め処理は小さな違いに見えて、請求額、税額、集計結果、レポート数値に大きく影響します。C#でMath.Roundを使うときは、丸め方法・桁数・数値型を明確にして実装しましょう。