我有以下代码
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等
答案 0 :(得分:2)
此问题中观察到的问题主要是由Microsoft选择格式引起的,特别是Microsoft软件无法显示准确值,因为即使格式字符串要求输入更多位数,它也限制了用于转换为十进制的位数。此外,与转换float
相比,转换double
时使用的位数更少。因此,如果格式化具有相同值的float
和double
,则结果可能会有所不同,因为float
格式将使用较少的有效数字。
下面,我一步一步地研究问题中的代码语句。总而言之,问题的症结在于,值61.0099983215332当其为float
时被格式化为“ 61.0100000000000”,而当其为double
时被格式化为“ 61.0099983215332”。这纯粹是Microsoft的格式选择,并非由浮点运算的性质引起。
语句double temp3 = 61.01
将temp3
初始化为正好61.00999999999999801048033987171947956085205078125。由于二进制浮点格式的性质,必须从61.01开始进行此更改-它不能精确表示61.01,因此使用double
中可表示的最接近值。
语句dynamic temp = 61.01f
将temp
初始化为完全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
。微软的文档有点含糊。它说,默认情况下,仅返回七(十进制)个精度数字。它说使用float
或G
格式最多获取9个,而R
既不使用F20
也不使用G
。因此,我相信只使用了七个数字。当61.009998321533203125四舍五入到七个有效十进制数字时,结果为“ 61.01000”。然后,R
方法将其填充到小数点后的20位,从而产生“ 61.01000000000000000000”。
我接下来将处理您的第三条ToString
陈述,然后再回到第二条。
语句WriteLine
格式化名为Console.WriteLine(String.Format(" {0:F20}", temp3));
的{{1}}。由于double
是temp3
,因此称为Double.ToString
。此方法使用15位精度(除非使用temp3
或double
)。将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位。因此,通过转换,您基本上是在砍掉位,直到可以将其放入浮点数为止。因此,这就是您降低精度的原因。