C# では、の結果Math.Round(2.5)
は 2 です。
3のはずですよね?なぜC#ではなく2なのですか?
まず、これは C# のバグではなく、.NET のバグです。Math.Round
C# は言語であり、実装方法を決定するものではありません。
そして第二に、いいえ - docsを読むと、デフォルトの丸めが「偶数に丸める」(銀行家の丸め)であることがわかります。
戻り値
の型: System.Double
a に最も近い整数。a の小数部が 2 つの整数 (一方が偶数でもう一方が奇数) の中間にある場合、偶数が返されます。このメソッドはDouble
、整数型ではなく を返すことに注意してください。備考
このメソッドの動作は、IEEE 標準 754 のセクション 4 に準拠しています。中間値を一方向に一貫して丸めることに起因する丸め誤差を最小限に抑えます。
値を取るオーバーロードMath.Round
を使用して、中間点を丸める方法を指定できます。1 つを持たない各オーバーロードに対応する 1 つのオーバーロードがあります。MidpointRounding
MidpointRounding
Round(Decimal)
/Round(Decimal, MidpointRounding)
Round(Double)
/Round(Double, MidpointRounding)
Round(Decimal, Int32)
/Round(Decimal, Int32, MidpointRounding)
Round(Double, Int32)
/Round(Double, Int32, MidpointRounding)
このデフォルトが適切に選択されたかどうかは別の問題です。( MidpointRounding
.NET 2.0 でのみ導入されました。それ以前は、自分で実行せずに目的の動作を実装する簡単な方法があったかどうかはわかりません。)特に、歴史は、それが期待される動作ではないことを示しています。ほとんどの場合、それはAPI 設計における大罪。Banker's Rounding が便利な理由はわかりますが、それでも多くの人にとっては驚きです。
RoundingMode
さらに多くのオプションを提供する最も近い Java の同等の列挙型 ( ) を見てみることに興味があるかもしれません。(中点だけを扱うわけではありません。)
MSDNから、Math.Round(double a)は次を返します。
a に最も近い整数。a の小数部が 2 つの整数 (一方が偶数でもう一方が奇数) の中間にある場合、偶数が返されます。
... 2 と 3 の中間にある 2.5 は、偶数 (2) に切り捨てられます。これはBanker's Rounding (または round-to-even) と呼ばれ、一般的に使用される丸め標準です。
同じ MSDN 記事:
このメソッドの動作は、IEEE 規格 754 のセクション 4 に従います。中間値を一方向に一貫して丸めることに起因する丸め誤差を最小限に抑えます。
モードを取る Math.Round のオーバーロードを呼び出すことで、別の丸め動作を指定できますMidpointRounding
。
MSDN で以下を確認する必要がありますMath.Round
。
このメソッドの動作は、IEEE 規格 754 のセクション 4 に従います。
Math.Round
オーバーロードを使用する動作を指定できます。
Math.Round(2.5, 0, MidpointRounding.AwayFromZero); // gives 3
Math.Round(2.5, 0, MidpointRounding.ToEven); // gives 2
分数を含む数値を、たとえば整数に丸めるタスクを考えてみましょう。この状況での丸めのプロセスは、どの整数が丸めている数値を最もよく表しているかを判断することです。
一般的な「算術」丸めでは、2.1、2.2、2.3、および 2.4 が 2.0 に丸められることは明らかです。および 2.6、2.7、2.8、および 2.9 ~ 3.0。
残りは 2.5 で、これは 2.0 に近づいているわけではなく、3.0 に近づいているわけでもありません。2.0 と 3.0 のどちらを選択するかはユーザー次第であり、どちらも等しく有効です。
マイナスの数値の場合、-2.1、-2.2、-2.3、および -2.4 は -2.0 になります。-2.6、2.7、2.8、および 2.9 は、算術丸めでは -3.0 になります。
-2.5 の場合、-2.0 と -3.0 の間で選択する必要があります。
その他の丸め
「切り上げ」は、小数点以下の桁数を取り、次の「整数」にします。したがって、2.5 と 2.6 を 3.0 に丸めるだけでなく、2.1 と 2.2 も丸めます。
切り上げは、正と負の両方の数値をゼロから遠ざけます。例えば。2.5 ~ 3.0 および -2.5 ~ -3.0。
「切り捨て」は、不要な桁を切り捨てて数値を切り捨てます。これは、数値をゼロに近づける効果があります。例えば。2.5 ~ 2.0 および -2.5 ~ -2.0
最も一般的な形式である「バンカーの丸め」では、丸めの結果が常に偶数になるように、丸められる .5 が切り上げまたは切り下げられます。したがって、2.5 は 2.0、3.5 から 4.0、4.5 から 4.0、5.5 から 6.0 などに丸められます。
「代替丸め」は、任意の .5 の処理を切り捨てと切り上げの間で交互に行います。
「ランダム丸め」は、完全にランダムに 0.5 を上下に丸めます。
対称性と非対称性
丸め関数は、すべての数値を 0 から遠ざけるように丸めるか、またはすべての数値を 0 に向けて丸める場合、「対称」であると言われます。
正の数をゼロに向けて丸め、負の数をゼロから遠ざける場合、関数は「非対称」です。2.5 から 2.0; そして-2.5から-3.0。
また、非対称は、正の数をゼロから遠ざけ、負の数をゼロに丸める関数です。例えば。2.5 から 3.0; そして-2.5から-2.0。
ほとんどの場合、-2.5 は -3.0 に丸められ、3.5 は 4.0 に丸められます。(C# でRound(AwayFromZero)
)
デフォルトMidpointRounding.ToEven
、または銀行家の四捨五入 ( 2.5 が 2 になる、4.5 が 4 になるなど) は、以前に会計報告書を書く際に私を悩ませたことがあります。この郵便受け。
ウィキペディアより
バンカーの丸めという用語の起源は、よりあいまいなままです。この丸め方法が銀行業務の標準であった場合、その証拠を見つけるのは非常に困難であることが証明されています。反対に、欧州委員会の報告書「ユーロの導入と通貨量の丸め」のセクション 2 は、以前は銀行業務における丸めに対する標準的なアプローチがなかったことを示唆しています。また、「中間」金額を切り上げる必要があることを指定します。
もちろん、銀行が偶数の預金を大量に受け取るために使用しない限り、特に銀行の丸め方法は非常に奇妙に思えます。240 万ポンドを入金しますが、それを 200 万ポンドと呼びます。
IEEE 標準 754 は 1985 年にさかのぼり、丸めの両方の方法を提供しますが、標準で推奨されているのはバンカーの方法です。このウィキペディアの記事には、言語が丸めを実装する方法の長いリストがあり (以下のいずれかが間違っている場合は修正してください)、ほとんどの場合、バンカーズを使用しませんが、学校で教えられている丸めを使用します。
Silverlight は MidpointRounding オプションをサポートしていないため、独自に作成する必要があります。何かのようなもの:
public double RoundCorrect(double d, int decimals)
{
double multiplier = Math.Pow(10, decimals);
if (d < 0)
multiplier *= -1;
return Math.Floor((d * multiplier) + 0.5) / multiplier;
}
これを拡張機能として使用する方法を含む例については、投稿を参照してください: .NET and Silverlight Rounding
C# アプリケーションでは切り上げられないのに、SQL サーバーでは 0.5 から 1 に切り上げられるというこの問題がありました。したがって、2 つの異なる結果が表示されます。
int/long を使用した実装を次に示します。これが Java の丸め方です。
int roundedNumber = (int)Math.Floor(d + 0.5);
これはおそらく、あなたが考えることができる最も効率的な方法でもあります。
double のままにして decimal precision を使用する場合は、小数点以下の桁数に基づいて 10 の指数を使用するだけです。
public double getRounding(double number, int decimalPoints)
{
double decimalPowerOfTen = Math.Pow(10, decimalPoints);
return Math.Floor(number * decimalPowerOfTen + 0.5)/ decimalPowerOfTen;
}
小数点に負の小数を入力することもできます。それも問題ありません。
getRounding(239, -2) = 200
.NET を使用した数値の丸めには、探している答えがあります。
基本的に、これはそれが言うことです:
戻り値
桁数に等しい精度を持つ値に最も近い数値。値が 2 つの数値の中間にあり、一方が偶数で他方が奇数の場合、偶数が返されます。value の精度が桁数未満の場合、value は変更されずに返されます。
このメソッドの動作は、IEEE 規格 754 のセクション 4 に従います。digits が 0 の場合、この種の丸めは 0 への丸めと呼ばれることがあります。
Silverlight は MidpointRounding オプションをサポートしていません。MidpointRounding 列挙型を追加する Silverlight の拡張メソッドを次に示します。
public enum MidpointRounding
{
ToEven,
AwayFromZero
}
public static class DecimalExtensions
{
public static decimal Round(this decimal d, MidpointRounding mode)
{
return d.Round(0, mode);
}
/// <summary>
/// Rounds using arithmetic (5 rounds up) symmetrical (up is away from zero) rounding
/// </summary>
/// <param name="d">A Decimal number to be rounded.</param>
/// <param name="decimals">The number of significant fractional digits (precision) in the return value.</param>
/// <returns>The number nearest d with precision equal to decimals. If d is halfway between two numbers, then the nearest whole number away from zero is returned.</returns>
public static decimal Round(this decimal d, int decimals, MidpointRounding mode)
{
if ( mode == MidpointRounding.ToEven )
{
return decimal.Round(d, decimals);
}
else
{
decimal factor = Convert.ToDecimal(Math.Pow(10, decimals));
int sign = Math.Sign(d);
return Decimal.Truncate(d * factor + 0.5m * sign) / factor;
}
}
}
ソース: http://anderly.com/2009/08/08/silverlight-midpoint-rounding-solution/
カスタムの丸めを使用する
public int Round(double value)
{
double decimalpoints = Math.Abs(value - Math.Floor(value));
if (decimalpoints > 0.5)
return (int)Math.Round(value);
else
return (int)Math.Floor(value);
}
これが私がそれを回避しなければならなかった方法です:
Public Function Round(number As Double, dec As Integer) As Double
Dim decimalPowerOfTen = Math.Pow(10, dec)
If CInt(number * decimalPowerOfTen) = Math.Round(number * decimalPowerOfTen, 2) Then
Return Math.Round(number, 2, MidpointRounding.AwayFromZero)
Else
Return CInt(number * decimalPowerOfTen + 0.5) / 100
End If
End Function
1.905 を小数点以下 2 桁で試すと、予想どおり 1.91 になりますがMath.Round(1.905,2,MidpointRounding.AwayFromZero)
、1.90 になります。Math.Round メソッドは完全に一貫性がなく、プログラマーが遭遇する可能性のある基本的な問題のほとんどに対して使用できません。(int) 1.905 * decimalPowerOfTen = Math.Round(number * decimalPowerOfTen, 2)
切り捨てられるべきものを切り上げたくない原因があるかどうかを確認する 必要があります。
これはまったく醜いですが、常に正しい算術丸めを生成します。
public double ArithRound(double number,int places){
string numberFormat = "###.";
numberFormat = numberFormat.PadRight(numberFormat.Length + places, '#');
return double.Parse(number.ToString(numberFormat));
}