【问题标题】:Converting float to double loses precision C#将浮点数转换为双精度会丢失 C#
【发布时间】:2018-12-07 05:13:21
【问题描述】:

我有以下代码

    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 等

【问题讨论】:

  • 我相当肯定答案仍将与this post 相同。您的源数据类型为float,而.01 无法用二进制准确表示:111101.000000101000111101 (conversion source)。
  • 顺便把格式改成G20,而不是F20
  • 我现在明白了。 F20是我的问题,通过使用G20我可以看到真正的价值。
  • @John:除了源文本的格式和转换之外,这个问题似乎没有任何浮点算术错误。这个问题似乎是微软格式化的结果,而不是浮点的性质。请不要随意关闭浮点问题作为that question 的重复项,因为这会妨碍人们获得正确答案。
  • @LasseVågsætherKarlsen:除了源文本的格式和转换之外,这个问题似乎没有任何浮点算术错误。这个问题似乎是微软格式化的结果,而不是浮点的性质。请不要随意关闭浮点问题作为that question 的重复项,因为这会妨碍人们获得正确答案。

标签: c# types casting floating-point


【解决方案1】:

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

下面,我一一浏览问题中的代码语句。综上所述,问题的症结在于,61.0099983215332的值在float时被格式化为“61.0100000000000”,在double时被格式化为“61.0099983215332”。这纯粹是微软对格式的选择,并不是浮点运算的性质造成的。

语句 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 转换为与temp 具有相同值的double,因此其值为61.009998321533203125。

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

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

接下来我将处理您的第三个WriteLine 声明,然后再回到第二个。

语句Console.WriteLine(String.Format(" {0:F20}", temp3)); 格式化了名为temp3double。因为temp3double,所以调用Double.ToString。此方法使用 15 位精度(除非使用GR)。当 61.00999999999999801048033987171947956085205078125 舍入到 15 位有效十进制数字时,结果为“61.0100000000000”。然后ToString 方法将其填充到小数点后的 20 位数字,生成“61.010000000000000000000”。

语句Console.WriteLine(String.Format(" {0:F20}", temp2)); 格式化了名为temp2doubletemp2 是一个 double,它包含来自 float temp 的值,因此它包含 61.009998321533203125。将其转换为 15 位有效十进制数字时,结果为“61.0099983215332”。然后ToString 方法将其填充到小数点后二十位,生成“61.00999832153320000000”。

最后,声明Console.WriteLine(String.Format(" {0:F20}", newValue)); 格式化newValue。将 .00000167846679488548033987171947956085205078125 格式化为 15 位有效数字会产生“0.00000167846679488548”。

【讨论】:

  • 这绝对是问题所在,我使用 F20 而不是 G20 混淆了手头的问题。感谢您花时间正确解释。
【解决方案2】:

这是因为浮点数分配给它们的位数少于双精度数。浮点数通常有 23 位,但双精度数有 52 位。因此,通过转换,您基本上是在切掉位,直到您可以将其放入浮点数中。所以这就是你失去精度的原因。

【讨论】:

  • 但是 OP 正在转换 float -> double,而不是 double -> float。
  • 我现在明白了。 F20是我的问题,通过使用G20我可以看到真正的价值。
猜你喜欢
  • 2011-08-24
  • 1970-01-01
  • 1970-01-01
  • 2014-09-28
  • 1970-01-01
  • 2015-06-12
  • 1970-01-01
  • 2011-05-26
  • 1970-01-01
相关资源
最近更新 更多