web-dev-qa-db-ja.com

C#の倍精度から10進数の精度損失

二重の_"138630.78380386264"_があり、それを10進数に変換したいのですが、その場合は、キャストまたはConvert.ToDecimal()を使用して行い、精度を失います。

どうしたの? 10進数とdoubleの両方がこの数値を保持できます。

enter image description here

_double doub = double.Parse("138630.78380386264");
decimal dec = decimal.Parse("138630.78380386264");
string decs = dec.ToString("F17");
string doubse =DoubleConverter.ToExactString(doub);
string doubs = doub.ToString("F17");

decimal decC = (decimal) doub;
string doudeccs = decC.ToString("F17");
decimal decConv = Convert.ToDecimal(doub);
string doudecs = decConv.ToString("F17");
_

また:デバッガーが示すのと同じ結果を出力するためにToString()をdoubleで取得するにはどうすればよいですか?例えば_138630.78380386264_?

30
GreyCloud

_138630.78380386264_は、倍精度で正確に表現できません。最も近い倍精度数(見つかった here )は_138630.783803862635977566242218017578125_であり、これは調査結果と一致します。

10進数への変換に精度が含まれない理由を尋ねます。 Convert.ToDecimal() のドキュメントには答えがあります:

このメソッドから返されるDecimal値には、最大15桁の有効数字が含まれています。 valueパラメータに15桁を超える有効数字が含まれている場合は、最も近い値に丸めることで丸められます。次の例は、Convert.ToDecimal(Double)メソッドが最も近い値に丸めることで、有効数字15桁のDecimal値を返す方法を示しています。

15の有効数字で最も近い値に四捨五入されたdouble値は、上記のとおり正確に_138630.783803863_です。

23
David Heffernan

残念なことだと思います。 139,000近くでは、DecimalDoubleよりもはるかに精度が高くなります。しかし、それでも、この問題のためにdifferentDoublesが同じDecimalに投影されています。例えば

double doub1 = 138630.7838038626;
double doub2 = 138630.7838038628;
Console.WriteLine(doub1 < doub2);                    // true, values differ as doubles
Console.WriteLine((decimal)doub1 < (decimal)doub2);  // false, values projected onto same decimal

実際には、上記のsix表現可能なDoublebetweendoub1doub2があるため、同じではありません。

これはいくぶんばかげた作業論です:

static decimal PreciseConvert(double doub)
{
  // Handle infinities and NaN-s first (throw exception)
  // Otherwise:
  return Decimal.Parse(doub.ToString("R"), NumberStyles.AllowExponent | NumberStyles.AllowDecimalPoint);
}

"R"フォーマット文字列は、マッピングを単射的にするのに十分な追加の数字が含まれることを保証します(Decimalが優れた精度を持つドメインで)。


一部の範囲では、longInt64)の精度がDoubleよりも高いことに注意してください。そこで、ここでの変換が同じ方法で行われるかどうかを確認しました(最初の小数点以下15桁に丸め)。ではない!そう:

double doub3 = 1.386307838038626e18;
double doub4 = 1.386307838038628e18;

Console.WriteLine(doub3 < doub4);              // true, values differ as doubles
Console.WriteLine((long)doub3 < (long)doub4);  // true, full precision of double used when converting to long

ターゲットがdecimalの場合、別の「ルール」を使用することに一貫性がないようです。

このため、(decimal)(long)doub3(decimal)doub3よりも正確な結果を生成することに注意してください。

5