尽管看起来很奇怪,但Convert.ToDecimal(float)
在某些情况下,通过小数(带 )进行转换可能是有益的。
如果知道原始数字是由用户以十进制表示形式提供的并且用户键入的有效数字不超过 7 个,它将提高精度。
为了证明这一点,我编写了一个小程序(见下文)。这是解释:
正如您从 OP 中回忆的那样,这是一系列步骤:
- 应用程序 B 的双精度数来自两个来源: (a) 计算结果;(b) 从用户输入的十进制数转换而来。
- 应用程序 B 将其双精度值作为浮点数写入文件 - 有效地进行从 52 个二进制数字(IEEE 754 single)到 23 个二进制数字(IEEE 754 double)的二进制舍入。
我们的应用程序读取该浮点数并通过以下两种方式之一将其转换为双精度数:
(a) 直接赋值给 double - 有效地将 23 位数字填充为具有二进制零(29 个零位)的 52 位数字;
(b) 通过用 转换为十进制(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.
因此,当我们转换为十进制时,我们会丢失十进制数字的 0.225。
所以,在一般情况下,当没有关于双精度的附加信息时,转换为十进制在大多数情况下会使我们失去一些精度。
但是(!)如果有额外的知识,最初(在作为浮点数写入文件之前)双精度数是不超过 7 位的小数,则在十进制舍入中引入的舍入错误(上面的步骤 3(b))将补偿二进制舍入引入的舍入误差(在上面的步骤 2. 中)。
在证明通用情况下的语句的程序中,我随机生成双精度数,然后将其转换为浮点数,然后将其直接转换回双精度(a),(b)通过十进制,然后我测量原始双精度与双 (a) 和双 (b)。如果 double(a) 比 double(b) 更接近原始值,我会增加 pro-direct 转换计数器,在相反的情况下,我会增加 pro-viaDecimal 计数器。我在 100 万的循环中进行。循环,然后我打印 pro-direct 与 pro-viaDecimal 计数器的比率。结果证明该比率约为 3.7,即大约在 5 分之四的情况下,通过十进制转换会破坏该数字。
为了证明用户输入数字时的情况,我使用了相同的程序,唯一的更改是应用于Math.Round(originalDouble, N)
双打。因为我从 Random 类中得到 originalDoubles,它们都将在 0 和 1 之间,所以有效位数与小数点后的位数一致。我把这个方法放在一个循环中,从用户输入的 1 个有效数字到 15 个有效数字。然后我把它画在图上。与用户输入的有效位数的依赖关系(直接转换比通过十进制转换好多少次)。
.
如您所见,对于 1 到 7 位键入的数字,通过 Decimal 的转换总是比直接转换更好。确切地说,对于一百万个随机数,只有 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();
}