奇妙に思えるかもしれませんが、10 進法 ( を使用Convert.ToDecimal(float)
) による変換は、状況によっては有益な場合があります。
元の数値がユーザーによって 10 進表現で提供され、ユーザーが入力した有効数字が 7 桁以下であることがわかっている場合は、精度が向上します。
それを証明するために、私は小さなプログラムを書きました (以下を参照)。説明は次のとおりです。
OPから思い出すと、これは一連のステップです。
- アプリケーション B には、次の 2 つのソースからの double があります。(a) 計算の結果。(b) ユーザーが入力した 10 進数から変換されます。
- アプリケーション B は double を float としてファイルに書き込みます - 52 の 2 進数 ( IEEE 754 single ) から 23 の 2 進数 ( IEEE 754 double ) へのバイナリ丸めを効果的に実行します。
私たちのアプリケーションはその float を読み取り、次の 2 つの方法のいずれかで double に変換します。
(a) double への直接代入 - 23 ビットの数値を 2 進数のゼロ (29 ビットのゼロ) で効果的に 52 ビットの数値にパディングします。
(b) で 10 進数に変換する(double)Convert.ToDecimal(float)
。
Ben Voigt が適切に気付いたようにConvert.ToDecimal(float)
( 「備考」セクションの MSDN を参照)、結果を 7 桁の有効桁数に丸めます。ウィキペディアのSingleに関する IEEE 754 の記事では、次のように読むことができますprecision is 24 bits - equivalent to log10(pow(2,24)) ≈ 7.225 decimal digits.
。
したがって、一般的なケースでは、double に関する追加情報がない場合、ほとんどの場合、10 進数への変換によって精度がいくらか失われます。
しかし (!) もともと (float としてファイルに書き込まれる前に) double が 7 桁以下の 10 進数であったという追加の知識がある場合、10 進数の丸め(上記のステップ 3(b))で導入された丸め誤差が補正されます。 2 進丸めで発生した丸め誤差(上記のステップ 2.)。
一般的なケースのステートメントを証明するプログラムでは、double をランダムに生成し、それを float にキャストし、(a) 直接 double に変換し、(b) decimal を介して変換し、元の double とダブル (a) とダブル (b)。double(a) が double(b) よりも元の値に近い場合は、pro-direct conversion カウンターをインクリメントし、逆の場合は pro-viaDecimal カウンターをインクリメントします。100万ループでやっています。サイクル、次に pro-direct と pro-viaDecimal カウンターの比率を出力します。比率は約 3.7 であることがわかります。つまり、5 つのケースのうち約 4 つのケースで、10 進数による変換によって数値が台無しになります。
ユーザーが数字を入力する場合を証明するために、同じプログラムを使用Math.Round(originalDouble, N)
し、ダブルスに適用する変更のみを行いました。originalDoubles は Random クラスから取得するため、すべて 0 から 1 の間になるため、有効桁数は小数点以下の桁数と一致します。このメソッドを、ユーザーが入力した有効数字 1 桁から有効数字 15 桁までの N によるループに配置しました。次に、それをグラフにプロットしました。ユーザーが入力した有効桁数からの依存性 (10 進数による変換よりも直接変換の方が優れている回数)。
.
ご覧のとおり、1 から 7 の型付き数字の場合、直接変換よりも Decimal による変換の方が常に優れています。正確には、100 万の乱数に対して、10 進数に変換しても改善されないのは 1 つまたは 2 つだけです。
比較に使用したコードは次のとおりです。
private static void CompareWhichIsBetter(int numTypedDigits)
{
Console.WriteLine("Number of typed digits: " + numTypedDigits);
Random rnd = new Random(DateTime.Now.Millisecond);
int countDecimalIsBetter = 0;
int countDirectIsBetter = 0;
int countEqual = 0;
for (int i = 0; i < 1000000; i++)
{
double origDouble = rnd.NextDouble();
//Use the line below for the user-typed-in-numbers case.
//double origDouble = Math.Round(rnd.NextDouble(), numTypedDigits);
float x = (float)origDouble;
double viaFloatAndDecimal = (double)Convert.ToDecimal(x);
double viaFloat = x;
double diff1 = Math.Abs(origDouble - viaFloatAndDecimal);
double diff2 = Math.Abs(origDouble - viaFloat);
if (diff1 < diff2)
countDecimalIsBetter++;
else if (diff1 > diff2)
countDirectIsBetter++;
else
countEqual++;
}
Console.WriteLine("Decimal better: " + countDecimalIsBetter);
Console.WriteLine("Direct better: " + countDirectIsBetter);
Console.WriteLine("Equal: " + countEqual);
Console.WriteLine("Betterness of direct conversion: " + (double)countDirectIsBetter / countDecimalIsBetter);
Console.WriteLine("Betterness of conv. via decimal: " + (double)countDecimalIsBetter / countDirectIsBetter );
Console.WriteLine();
}