web-dev-qa-db-ja.com

C#でのdouble値の丸め

C#でdouble値の丸め方法が必要です。 double値を任意の丸め精度値に丸めることができる必要があります。手元のコードは次のようになります。

public static double RoundI(double number, double roundingInterval) {

    if (roundingInterval == 0.0)
    {
        return;
    }

    double intv = Math.Abs(roundingInterval);
    double sign = Math.Sign(number);
    double val = Math.Abs(number);

    double valIntvRatio = val / intv;
    double k = Math.Floor(valIntvRatio);
    double m = valIntvRatio - k;

    bool mGreaterThanMidPoint = ((m - 0.5) >= 1e-14) ? true : false;
    bool mInMidpoint = (Math.Abs(m - 0.5) < 1e-14) ? true : false;
    return (mGreaterThanMidPoint || mInMidpoint) ? sign * ((k + 1) * intv) : sign * (k * intv);
}

したがって、RoundI(100、3)は99を与え、RoundI(1.2345、0.001)は1.235を与えます。

問題は、RoundI(1.275、0.01)が1.28ではなく1.27を返すことです。これは、double valIntvRatio = val/intv、つまりdouble valIntvRatio = 1.275/0.01を実行すると、0.12749999999999になるためです。これは、どのプログラミング言語でも二重表現の問題であることを知っています。私の質問は、doubleの精度について心配する必要なく、このようなことを行う標準コードはありますか?ここでは、許容値を1e-14に設定していますが、これはこの問題に対して制限が多すぎ、設定する正しい許容値がわかりません。助けてくれてありがとう.

30
Steve

Kibbeeとしてのdecimalの使用例

double d = 1.275;
Math.Round(d, 2);          // 1.27
Math.Round((decimal)d, 2); // 1.28 
42
Jimmy
double d = 1.2345;

Math.Round(d, 2);

上記のコードでうまくいくはずです。

5
AceMark

実際にdoubleを使用する必要がある場合は、以下で置き換えるだけで機能しますが、2進浮動小数点演算の通常の精度の問題があります。

以下の私の文字列ジャグリングよりも「丸め」(ほとんどの種類の銀行員の丸め)を実装するためのより良い方法は確かにあります。

public static decimal RoundI(decimal number, decimal roundingInterval)
{
   if (roundingInterval == 0) { return 0;}

   decimal intv = Math.Abs(roundingInterval);
   decimal modulo = number % intv;
   if ((intv - modulo) == modulo) {
       var temp = (number - modulo).ToString("#.##################");
       if (temp.Length != 0 && temp[temp.Length - 1] % 2 == 0) modulo *= -1;
   }
    else if ((intv - modulo) < modulo)
        modulo = (intv - modulo);
    else
        modulo *= -1;

    return number + modulo;
}
2
Jonas Elfström

Jimmyの回答で提供されている10進キャストを使用する例は、要求に応じてdouble値を丸め精度値に丸める方法を示していないため、質問には答えません。 10進数キャストを使用した正しい答えは次のとおりです。

    public static double RoundI(double number, double roundingInterval)
    {
        return (double)((decimal)roundingInterval * Math.Round((decimal)number / (decimal)roundingInterval, MidpointRounding.AwayFromZero));
    }

10進数のキャストを使用するため、このソリューションは、コメントでJeppe Stig NielsenJimmyの回答に言及しているキャストエラーの影響を受けます。

また、MidpointRounding.AwayFromZeroを指定したことに注意してください。これは、RoundI(1.2345、0.001)が1.235を与える必要があるというリクエスターの仕様と一致しているためです。

2
R.T.