将float转换为double会失去精度C#

时间:2018-12-07 05:13:21

标签: c# types casting floating-point

我有以下代码

    double temp3 = 61.01;

    //This can actually be various other types, but float is the one that causes problems
    dynamic temp = 61.01f;
    double temp2 = (double)Convert.ChangeType(temp, typeof(double));

    double newValue = temp2 - temp3;

    //newValue should == 0 but it does not

    Console.WriteLine(String.Format("  {0:F20}", temp));
    Console.WriteLine(String.Format("  {0:F20}", temp2));
    Console.WriteLine(String.Format("  {0:F20}", temp3));
    Console.WriteLine(String.Format("  {0:F20}", newValue));

哪个生产

61.01000000000000000000

61.00999832153320000000

61.01000000000000000000

-0.00000167846679488548

为什么Convert.ChangeType失去精度?

我们使用Convert.ChangeType是因为使用了动态变量,该变量可以是byte / uint / float / double等

2 个答案:

答案 0 :(得分:2)

此问题中观察到的问题主要是由Microsoft选择格式引起的,特别是Microsoft软件无法显示准确值,因为即使格式字符串要求输入更多位数,它也限制了用于转换为十进制的位数。此外,与转换float相比,转换double时使用的位数更少。因此,如果格式化具有相同值的floatdouble,则结果可能会有所不同,因为float格式将使用较少的有效数字。

下面,我一步一步地研究问题中的代码语句。总而言之,问题的症结在于,值61.0099983215332当其为float时被格式化为“ 61.0100000000000”,而当其为double时被格式化为“ 61.0099983215332”。这纯粹是Microsoft的格式选择,并非由浮点运算的性质引起。

语句double temp3 = 61.01temp3初始化为正好61.00999999999999801048033987171947956085205078125。由于二进制浮点格式的性质,必须从61.01开始进行此更改-它不能精确表示61.01,因此使用double中可表示的最接近值。

语句dynamic temp = 61.01ftemp初始化为完全61.009998321533203125。与double一样,使用了最接近的可表示值,但是,由于float的精度较低,因此最接近的值不像double情况下那样接近。

语句double temp2 = (double)Convert.ChangeType(temp, typeof(double));temp转换为具有与double相同值的temp,因此其值是61.009998321533203125。

语句double newValue = temp2 - temp3;正确地减去了两个值,产生了准确的结果0.00000167846679488548033987171947956085205078125,没有错误。

语句Console.WriteLine(String.Format(" {0:F20}", temp));格式化名为float的{​​{1}}。格式化temp涉及调用Single.ToString。微软的文档有点含糊。它说,默认情况下,仅返回七(十进制)个精度数字。它说使用floatG格式最多获取9个,而R既不使用F20也不使用G。因此,我相信只使用了七个数字。当61.009998321533203125四舍五入到七个有效十进制数字时,结果为“ 61.01000”。然后,R方法将其填充到小数点后的20位,从而产生“ 61.01000000000000000000”。

我接下来将处理您的第三条ToString陈述,然后再回到第二条。

语句WriteLine格式化名为Console.WriteLine(String.Format(" {0:F20}", temp3));的{​​{1}}。由于doubletemp3,因此称为Double.ToString。此方法使用15位精度(除非使用temp3double)。将61.00999999999999801048033987171947956085205078125舍入为15个有效十进制数字时,结果为“ 61.0100000000000”。然后,G方法将其填充到小数点后的20位,从而产生“ 61.01000000000000000000”。

语句R格式化名为ToString的{​​{1}}。 Console.WriteLine(String.Format(" {0:F20}", temp2));是一个double,其中包含temp2 temp2中的值,因此它包含61.009998321533203125。将其转换为15个有效十进制数字时,结果为“ 61.0099983215332”。然后,double方法会将其填充到小数点后的20位,从而产生“ 61.00999832153153320000000”。

最后,语句float的格式为temp。将.00000167846679488548033987171947956085205078125格式化为15个有效数字将产生“ 0.00000167846679488548”。

答案 1 :(得分:-1)

这是因为浮点数分配给它们的位数少于双精度数。浮点数通常有23位,而双精度数通常有52位。因此,通过转换,您基本上是在砍掉位,直到可以将其放入浮点数为止。因此,这就是您降低精度的原因。