我们现有的应用程序从文件中读取一些浮点数.这些数字是由其他一些应用程序写的(我们称之为应用程序B).此文件的格式很久以前就已修复(我们无法更改).在此文件中,所有浮点数都以二进制表示形式保存为浮点数(文件中为4个字节).
在我们的程序中,一旦我们读取数据,我们就将浮点数转换为双精度数并使用双精度数进行所有计算,因为计算非常广泛,我们关注舍入误差的传播.
我们注意到,当我们通过十进制转换浮点数时(参见下面的代码),我们得到的结果比直接转换时更精确.注意:应用程序B也在内部使用双精度数,只将它们作为浮点数写入文件中.假设应用程序B将数字0.012写入文件为float.如果我们在读取到十进制后转换它然后加倍我们得到0.012,如果我们直接转换它,我们得到0.0120000001043081.
这可以在不读取文件的情况下重现 - 只需一个作业:
float readFromFile = 0.012f;
Console.WriteLine("Read from file: " + readFromFile);
//prints 0.012
double forUse = readFromFile;
Console.WriteLine("Converted to double directly: " + forUse);
//prints 0.0120000001043081
double forUse1 = (double)Convert.ToDecimal(readFromFile);
Console.WriteLine("Converted to double via decimal: " + forUse1);
//prints 0.012
Run Code Online (Sandbox Code Playgroud)
通过十进制从float转换为double总是有益的,如果不是,在什么条件下它是有益的?
编辑:应用程序B可以通过两种方式获取它保存的值:
Ben*_*igt 13
我们得到0.012
不,你没有.既float不能也double不能完全代表3/250.你得到什么是由字符串格式呈现的值Double.ToString()作为"0.012".但这是因为格式化程序不显示确切的值.
经历decimal导致四舍五入.使用Math.Round您想要的舍入参数可能会快得多(更不用说更容易理解)了.如果您关心的是有效位数,请参阅:
对于它的价值,0.012f(这意味着32位IEEE-754值最接近0.012)正是如此
0x3C449BA6
Run Code Online (Sandbox Code Playgroud)
要么
0.012000000104308128
Run Code Online (Sandbox Code Playgroud)
这完全可以表示为System.Decimal.但是Convert.ToDecimal(0.012f)不会给你那个确切的价值 - 根据文件有一个舍入步骤.
Decimal此方法返回的值最多包含七位有效数字.如果value参数包含七位以上的有效数字,则使用舍入舍入为最接近的数字.
尽管看起来很奇怪,但通过十进制(使用Convert.ToDecimal(float))进行转换在某些情况下可能是有益的。
如果知道原始数字是由用户以十进制表示形式提供的并且用户输入的有效数字不超过 7 位,则将提高精度。
\n\n为了证明这一点,我编写了一个小程序(见下文)。解释如下:
\n\n正如您从 OP 中回忆的那样,这是步骤顺序:
\n\n我们的应用程序读取该浮点数并通过以下两种方式之一将其转换为双精度数:
\n\n(a) 直接赋值给 double - 用二进制零(29 个零位)有效地将 23 位数字填充为 52 位数字;
\n\n(b) 通过使用 转换为十进制(double)Convert.ToDecimal(float)。
正如 Ben Voigt 正确注意到的那样Convert.ToDecimal(float)(请参阅 MSDN 的“备注”部分)将结果四舍五入到 7 位有效小数位。在 Wikipedia 关于 Single 的IEEE 754 文章中,我们可以读到precision is 24 bits - equivalent to log10(pow(2,24)) \xe2\x89\x88 7.225 decimal digits.,因此,当我们转换为十进制时,我们会丢失 0.225 个十进制数字。
因此,在一般情况下,当没有有关双精度数的附加信息时,转换为十进制在大多数情况下会使我们失去一些精度。
\n\n但是(!)如果有额外的知识,最初(在作为浮点数写入文件之前)双精度数是不超过 7 位的小数,则小数舍入中引入的舍入误差(上面的步骤 3(b))将补偿二进制舍入引入的舍入误差(在上面的步骤 2 中)。
\n\n在程序中证明该语句为通用情况我随机生成双精度数,然后将其转换为浮点数,然后将其直接转换回双精度数(a),(b)通过十进制,然后测量原始双精度数和双 (a) 和双 (b)。如果 double(a) 比 double(b) 更接近原始值,我将增加 pro-direct conversion 计数器,在相反的情况下,我将增加 pro-viaDecimal 计数器。我以 100 万次循环执行此操作。周期,然后我打印 pro-direct 与 pro-viaDecimal 计数器的比率。结果该比率约为 3.7,即大约五分之四的情况下,通过十进制转换会破坏该数字。
\n\n为了证明用户输入数字时的情况,我使用了相同的程序,唯一的更改是应用于Math.Round(originalDouble, N)双打。因为我从Random类中得到originalDoubles,它们都在0和1之间,所以有效位数与小数点后的位数一致。我将此方法放在一个循环中,由用户输入 N 个有效数字到 15 个有效数字。然后我把它画在图表上。(直接转换比通过十进制转换好多少次)与用户键入的有效位数的相关性。\n
。
正如您所看到的,对于 1 到 7 个键入的数字,通过十进制转换总是比直接转换更好。准确地说,对于一百万个随机数,只有 1 或 2 个没有通过转换为十进制而得到改善。
\n\n这是用于比较的代码:
\n\nprivate static void CompareWhichIsBetter(int numTypedDigits)\n{\n Console.WriteLine("Number of typed digits: " + numTypedDigits);\n Random rnd = new Random(DateTime.Now.Millisecond);\n int countDecimalIsBetter = 0;\n int countDirectIsBetter = 0;\n int countEqual = 0;\n\n for (int i = 0; i < 1000000; i++)\n {\n double origDouble = rnd.NextDouble();\n //Use the line below for the user-typed-in-numbers case.\n //double origDouble = Math.Round(rnd.NextDouble(), numTypedDigits); \n\n float x = (float)origDouble;\n double viaFloatAndDecimal = (double)Convert.ToDecimal(x);\n double viaFloat = x;\n\n double diff1 = Math.Abs(origDouble - viaFloatAndDecimal);\n double diff2 = Math.Abs(origDouble - viaFloat);\n\n if (diff1 < diff2)\n countDecimalIsBetter++;\n else if (diff1 > diff2)\n countDirectIsBetter++;\n else\n countEqual++;\n }\n\n Console.WriteLine("Decimal better: " + countDecimalIsBetter);\n Console.WriteLine("Direct better: " + countDirectIsBetter);\n Console.WriteLine("Equal: " + countEqual);\n Console.WriteLine("Betterness of direct conversion: " + (double)countDirectIsBetter / countDecimalIsBetter);\n Console.WriteLine("Betterness of conv. via decimal: " + (double)countDecimalIsBetter / countDirectIsBetter );\n Console.WriteLine();\n}\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
962 次 |
| 最近记录: |