C#における小数点の丸め処理
C#のMath.Floor()メソッドとMath.Ceiling()メソッドは、それぞれ数値を切り捨て(床関数)、切り上げ(天井関数)して整数にするための標準的な関数です。
Math.Floor(10.7)は10になります。Math.Ceiling(10.2)は11になります。
しかし、実務的な計算では、「小数点第3位以下を切り捨てたい」や「小数点第2位以下を切り上げたい」といった、整数(0桁)以外の位置での丸め処理が必要になることがよくあります。
C#の標準ライブラリには、Math.Floor(value, decimalPlaces) のような、桁数を指定できる組み込みメソッドは用意されていません。
この記事では、Math.Pow()(べき乗)を組み合わせて、指定した小数点以下の桁数でFloor(切り捨て)およびCeiling(切り上げ)を実現するロジックを解説します。
指定桁数で切り捨て (Math.Floor)
Math.Floorを使用して指定した桁数(例: dp桁)で切り捨てを行うには、以下のステップを実行します。
Math.Pow(10, dp)を使い、10のdp乗(dp=2なら100)を計算します。- 元の数値にこの乗数を掛け、小数点の位置を
dp桁右にずらします。 - その結果に対して
Math.Floorを適用し、小数点以下を切り捨てます。 - 最後に、1.で使った乗数で割り、小数点の位置を元に戻します。
コード例: Floor
例えば、複数のセンサーから取得したデータの平均値を計算し、小数点第3位以下を切り捨てて(第3位までを有効な数値として)記録するシナリオです。
using System;
public class FloorDecimalExample
{
public static void Main()
{
double totalSensorValue = 845.6;
int sensorCount = 13;
// 845.6 / 13 = 65.046153846...
double averageValue = totalSensorValue / sensorCount;
// 小数点第3位 (dp=3) で切り捨てたい
int decimalPlaces = 3;
// 1. 10の3乗 (1000) を計算
double multiplier = Math.Pow(10, decimalPlaces);
// 2. 小数点をずらす (65.04615... * 1000 = 65046.15...)
// 3. 切り捨て (Math.Floor(65046.15...) = 65046.0)
// 4. 小数点を戻す (65046.0 / 1000 = 65.046)
double flooredAverage = Math.Floor(averageValue * multiplier) / multiplier;
Console.WriteLine($"計算結果 (生): {averageValue}");
Console.WriteLine($"第3位で切り捨て: {flooredAverage}");
}
}
出力結果:
計算結果 (生): 65.04615384615385
第3位で切り捨て: 65.046
指定桁数で切り上げ (Math.Ceiling)
Math.CeilingのロジックもFloorと全く同じですが、ステップ3で使用するメソッドがMath.Ceilingに変わります。
コード例: Ceiling
例えば、ある計算結果(65.04615...)を、小数点第3位までを含むように「切り上げる」必要がある場合のロジックです。
using System;
public class CeilingDecimalExample
{
public static void Main()
{
double averageValue = 65.04615384615385; // 上記の例の計算結果
int decimalPlaces = 3;
double multiplier = Math.Pow(10, decimalPlaces); // 1000
// 2. 小数点をずらす (65046.15...)
// 3. 切り上げ (Math.Ceiling(65046.15...) = 65047.0)
// 4. 小数点を戻す (65047.0 / 1000 = 65.047)
double ceilingAverage = Math.Ceiling(averageValue * multiplier) / multiplier;
Console.WriteLine($"計算結果 (生): {averageValue}");
Console.WriteLine($"第3位で切り上げ: {ceilingAverage}");
}
}
出力結果:
計算結果 (生): 65.04615384615385
第3位で切り上げ: 65.047
負の数の場合の動作
この手法の重要な点は、Math.Floor(負の無限大方向への丸め)とMath.Ceiling(正の無限大方向への丸め)の特性が、指定した桁数でも維持されることです。
これは、「0方向への切り捨て(Math.Truncate)」とは異なる動作になります。
using System;
public class NegativeExample
{
public static void Main()
{
double negativeValue = -10.12345;
int dp = 3;
double multiplier = Math.Pow(10, dp); // 1000
// -10.12345 * 1000 = -10123.45
// Floor: -10123.45 より小さい整数 = -10124
// -10124 / 1000 = -10.124
double floored = Math.Floor(negativeValue * multiplier) / multiplier;
// Ceiling: -10123.45 より大きい整数 = -10123
// -10123 / 1000 = -10.123
double ceiling = Math.Ceiling(negativeValue * multiplier) / multiplier;
Console.WriteLine($"元の値: {negativeValue}");
Console.WriteLine($"Floor (負の無限大方向): {floored}");
Console.WriteLine($"Ceiling (正の無限大方向): {ceiling}");
}
}
出力結果:
元の値: -10.12345
Floor (負の無限大方向): -10.124
Ceiling (正の無限大方向): -10.123
補足: decimal型での計算
Math.Pow()はdouble型を扱うため、高精度が求められる金融計算(decimal型)には注意が必要です。decimal型でこのロジックを使用する場合、Math.Pow()の結果をdecimalにキャストするか、乗数を100mや1000mのようにdecimalリテラルとして明示的に定義する方が安全です。
decimal price = 1.01234m;
decimal multiplier = 1000m; // 10の3乗
// Math.Floor は decimal にも対応している
decimal flooredPrice = Math.Floor(price * multiplier) / multiplier;
Console.WriteLine($"Decimal (Floor): {flooredPrice}"); // 1.012
まとめ
Math.FloorとMath.Ceilingには桁数を指定する機能はありませんが、Math.Pow(または100mのような固定値)を使って「小数点をずらす → 丸める → 小数点を戻す」という手順を踏むことで、指定した桁数での切り捨て・切り上げを正確に実装できます。
このテクニックは、Math.Round(四捨五入)とは異なる、特定の方向への丸め処理(切り捨て・切り上げ)が必要な場合に不可欠です。
