C#における数値の「丸め」
C#でdoubleやdecimalといった小数点以下の数値を持つデータを扱う際、計算結果を指定した桁数に「丸める」処理は不可欠です。一般的に「四捨五入」と呼ばれる処理ですが、C#の標準メソッドであるMath.Roundは、デフォルトの動作が一般的な四捨五入とは異なるため、注意が必要です。
この記事では、Math.Roundメソッドの基本的な使い方と、丸め処理の規則を制御するMidpointRoundingについて詳しく解説します。
Math.Round の基本的な使い方
Math.Roundメソッドには、主に以下のオーバーロード(引数の形式)があります。
Math.Round(value): 値を整数(小数点以下0桁)に丸めます。Math.Round(value, digits): 値を指定したdigits(桁数)に丸めます。Math.Round(value, digits, mode): 値を指定した桁数に、mode(丸め規則)で丸めます。
デフォルトの動作:「銀行家の丸め」 (MidpointRounding.ToEven)
Math.Roundメソッドで丸め規則(mode)を省略した場合、デフォルトで MidpointRounding.ToEven(最近接偶数への丸め)、通称「銀行家の丸め」が適用されます。
これは、丸める桁の数値がちょうど.5(中間点)であった場合に、丸めた結果が偶数になる方向へ丸める規則です。
Math.Round(12.5)の結果は12になります(12が偶数)。Math.Round(13.5)の結果は14になります(14が偶数)。
この方法は、大量のデータを集計する際に、.5を常に切り上げることによって生じる統計的な偏り(バイアス)を最小限に抑えるために、科学技術計算や金融分野で標準的に用いられます。
ToEven(デフォルト)のコード例
小数点第2位に丸める(digits = 2)場合の例です。
using System;
public class ToEvenRoundingExample
{
public static void Main()
{
// 銀行家の丸め (デフォルト)
decimal value1 = 10.125m; // 中間点 (.005)
decimal value2 = 10.135m; // 中間点 (.005)
// value1: .125 -> 10.12 (12は偶数)
decimal result1 = Math.Round(value1, 2);
// value2: .135 -> 10.14 (14は偶数)
decimal result2 = Math.Round(value2, 2);
Console.WriteLine($"--- 銀行家の丸め (ToEven) ---");
Console.WriteLine($"{value1} -> {result1}");
Console.WriteLine($"{value2} -> {result2}");
}
}
出力結果:
--- 銀行家の丸め (ToEven) ---
10.125 -> 10.12
10.135 -> 10.14
一般的な「四捨五入」:MidpointRounding.AwayFromZero
もし、いわゆる「四捨五入」(.5の場合は常に切り上げる)を行いたい場合は、Math.Roundの第3引数に MidpointRounding.AwayFromZero を明示的に指定する必要があります。
このモードは、中間点(.5)の場合、0(ゼロ)から遠ざかる方向に丸めます。
12.5は13になります(0から遠ざかる)。13.5は14になります(0から遠ざかる)。-12.5は-13になります(0から遠ざかる)。
AwayFromZero のコード例
using System;
public class AwayFromZeroRoundingExample
{
public static void Main()
{
// 四捨五入
decimal value1 = 10.125m; // 中間点 (.005)
decimal value2 = 10.135m; // 中間点 (.005)
decimal value3 = -10.125m; // 負の中間点
// value1: .125 -> 10.13 (切り上げ)
decimal result1 = Math.Round(value1, 2, MidpointRounding.AwayFromZero);
// value2: .135 -> 10.14 (切り上げ)
decimal result2 = Math.Round(value2, 2, MidpointRounding.AwayFromZero);
// value3: -10.125 -> -10.13 (0から遠ざかる方向へ)
decimal result3 = Math.Round(value3, 2, MidpointRounding.AwayFromZero);
Console.WriteLine($"--- 四捨五入 (AwayFromZero) ---");
Console.WriteLine($"{value1} -> {result1}");
Console.WriteLine($"{value2} -> {result2}");
Console.WriteLine($"{value3} -> {result3}");
}
}
出力結果:
--- 四捨五入 (AwayFromZero) ---
10.125 -> 10.13
10.135 -> 10.14
-10.125 -> -10.13
2つのモードの比較
以下のコードは、複数の値に対して2つの丸め規則を適用した結果を比較します。
using System;
public class RoundingComparison
{
public static void Main()
{
// 比較対象の数値配列
var valuesToRound = new double[] { 4.12, 4.15, 4.25, 4.18 };
int digits = 1; // 小数点第1位に丸める
Console.WriteLine("元の値 | AwayFromZero (四捨五入) | ToEven (銀行家の丸め)");
Console.WriteLine("-----------------------------------------------------");
foreach (var val in valuesToRound)
{
var roundAway = Math.Round(val, digits, MidpointRounding.AwayFromZero);
var roundEven = Math.Round(val, digits, MidpointRounding.ToEven);
Console.WriteLine($"{val:F2} | {roundAway:F1} | {roundEven:F1}");
}
}
}
出力結果:
元の値 | AwayFromZero (四捨五入) | ToEven (銀行家の丸め)
-----------------------------------------------------
4.12 | 4.1 | 4.1
4.15 | 4.2 | 4.2
4.25 | 4.3 | 4.2
4.18 | 4.2 | 4.2
4.15(中間点)はAwayFromZeroでは4.2、ToEvenでも(結果が偶数のため)4.2になります。 4.25(中間点)はAwayFromZeroでは4.3になりますが、ToEvenでは(結果が偶数になるよう)4.2になります。
まとめ
C#のMath.Roundは強力な丸めメソッドですが、そのデフォルト動作はMidpointRounding.ToEven(銀行家の丸め)であり、一般的な「四捨五入」とは異なります。
- 銀行家の丸め(統計的な偏りが少ない)が必要な場合:
Math.Round(value, digits)(デフォルト)を使用します。 - 一般的な四捨五入(.5は常に0から遠ざかる)が必要な場合:
Math.Round(value, digits, MidpointRounding.AwayFromZero)を明示的に指定します。
計算要件に応じて、正しい丸めモードを選択することが非常に重要です。
