C# 什么时候通过十进制从浮点转换为双精度有益

C# 什么时候通过十进制从浮点转换为双精度有益,c#,rounding,C#,Rounding,我们现有的应用程序从文件中读取一些浮点数。这些数字是由其他应用程序(我们称之为应用程序B)写入的。这个文件的格式很久以前就被修复了(我们无法更改它)。在该文件中,所有浮点数都以二进制表示形式保存为浮点数(文件中为4字节) 在我们的程序中,当我们读取数据时,我们将浮点转换为双倍,并在所有计算中使用双倍,因为计算非常广泛,我们关心舍入误差的传播 我们注意到,当我们通过十进制转换浮点数时(见下面的代码),我们得到的结果比直接转换时更精确。注意:应用程序B在内部也使用double,并且只将它们作为flo

我们现有的应用程序从文件中读取一些浮点数。这些数字是由其他应用程序(我们称之为应用程序B)写入的。这个文件的格式很久以前就被修复了(我们无法更改它)。在该文件中,所有浮点数都以二进制表示形式保存为浮点数(文件中为4字节)

在我们的程序中,当我们读取数据时,我们将浮点转换为双倍,并在所有计算中使用双倍,因为计算非常广泛,我们关心舍入误差的传播

我们注意到,当我们通过十进制转换浮点数时(见下面的代码),我们得到的结果比直接转换时更精确。注意:应用程序B在内部也使用double,并且只将它们作为float写入文件。假设应用程序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
通过十进制从浮点转换为双精度总是有益的吗?如果不是,在什么条件下是有益的

编辑:应用程序B可以通过两种方式获得保存的值:

  • 值可以是计算的结果
  • 值可以由用户以小数形式输入(因此在上面的示例中,用户在编辑框中输入了0.012,并将其转换为double,然后保存为float)
  • 我们得到的正好是0.012

    不,你没有。
    float
    double
    都不能精确表示3/250。您得到的是字符串格式化程序
    Double.ToString()
    作为
    “0.012”
    呈现的值。但这是因为格式化程序没有显示确切的值

    通过
    十进制
    会导致四舍五入。只使用
    Math.Round
    和所需的舍入参数可能会更快(更不用说更容易理解)。如果您关心的是有效位数,请参阅:


    值得一提的是,
    0.012f
    (这意味着最接近0.012的32位IEEE-754值)正好是

    0x3C449BA6
    

    这可以精确地表示为
    系统.Decimal
    。但是
    Convert.ToDecimal(0.012f)
    不会给出准确的值--per

    此方法返回的
    十进制
    值最多包含七位有效数字。如果值参数包含七个以上的有效数字,则使用舍入到最接近值对其进行舍入


    这里有一个不同的答案——我不确定它是否比Ben的好(几乎可以肯定不是),但它应该产生正确的结果:

    float readFromFile = 0.012f;
    decimal forUse = Convert.ToDecimal(readFromFile.ToString("0.000"));
    
    只要
    .ToString(“0.000”)
    生成了“正确”的数字(应该很容易抽查),那么您就可以使用它,而不必担心舍入错误。如果需要更高的精度,只需添加更多的
    0


    当然,如果您确实需要将
    0.012f
    转换到最大精度,那么这不会有什么帮助,但如果是这样,那么您首先就不想将其从浮点转换为十进制。

    看起来很奇怪,通过十进制转换(使用
    转换.ToDecimal(float)
    )在某些情况下可能是有益的

    如果知道原始数字是由用户以十进制表示提供的,并且用户键入的有效数字不超过7位,则将提高精度。

    为了证明这一点,我编写了一个小程序(见下文)。解释如下:

    正如您从OP中回忆到的,这是一系列步骤:

  • 应用程序B具有来自两个来源的双重功能: (a) 计算结果;(b) 从用户键入的十进制数转换而来
  • 应用程序B将它的双精度浮点值写入文件-有效 从52位二进制数字()到23位二进制数字()进行二进制舍入
  • 我们的应用程序读取该浮点并通过以下两种方式之一将其转换为double:

    (a) 直接赋值,用二进制零(29个零位)将23位数字加倍有效地填充到52位数字

    (b) 通过使用
    (双精度)Convert.ToDecimal(float)
    转换为十进制

  • 正如Ben Voigt正确注意到的那样,
    Convert.ToDecimal(float)
    ()将结果舍入到7位有效的十进制数字。在维基百科的about Single中,我们可以看到
    精度是24位-相当于log10(pow(2,24))≈ 7.225位小数。
    因此,当我们转换为小数时,我们会丢失0.225位小数

    因此,在一般情况下,当没有关于双精度的附加信息时,在大多数情况下转换为十进制会使我们失去一些精度。

    但是(!)如果有额外的知识,最初(在作为浮点写入文件之前)双精度是不超过7位的小数,在小数舍入中引入的舍入误差(上面的步骤3(b)将补偿在二进制舍入中引入的舍入误差(在上述步骤2中)

    在证明一般情况下语句的程序中,我随机生成double,然后将其转换为float,然后直接将其转换回double(a),通过十进制(b),然后测量原始double与double(a)和double(b)之间的距离。如果double(a)比double(b)更接近原始double(b),我增加pro-direct转换计数器,在相反的情况下,我增加pro-viaDecimal计数器。我在一个1百万个周期的循环中进行,然后我打印pro-direct与pro-viaDecimal计数器的比率。比率大约为3.7,即大约在5个循环中的4个循环中
    float readFromFile = 0.012f;
    decimal forUse = Convert.ToDecimal(readFromFile.ToString("0.000"));
    
    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();
    }