【问题标题】:Casting float to decimal loses precision in C#将浮点数转换为十进制会在 C# 中失去精度
【发布时间】:2012-01-07 05:30:10
【问题描述】:

在 C# 4.0 中,以下转换的行为非常出乎意料:

(decimal)1056964.63f
1056965

强制转换为 double 效果很好:

(double)1056964.63f
1056964.625

(decimal)(double)1056964.63f
1056964.625

这是设计的吗?

【问题讨论】:

标签: c# .net casting floating-point decimal


【解决方案1】:

问题在于您的初始值 - float 无论如何只能精确到 7 位有效十进制数字:

float f = 1056964.63f;
Console.WriteLine(f); // Prints 1056965

所以真的第二个的例子在某些方面是出乎意料的。

现在f 中的精确 值是 1056965.625,但这是为从大约 1056964.563 到 1056964.687 的所有值给出的值 - 所以即使是“.6”部分也不总是正确的。这就是为什么System.Single state 的文档:

默认情况下,Single 值仅包含 7 位精度的十进制数字,但内部维护最多 9 位数字。

当您转换为 double 时,额外的信息仍会保留,因为这可以在完全不“解释”它的情况下保留它 - 将其转换为十进制形式(打印或 decimal 类型)去通过知道它不能“信任”最后两位数字的代码。

【讨论】:

  • 不正确。在这种情况下,float 实际上可以保存值1056964.63,它是将其四舍五入为 1056965 的格式。来自 SSCLI 中 COMNumber::FormatSingle 的引用:“为了提供既易于显示又可往返的数字,我们解析number 使用 7 位数字,然后确定它是否往返到相同的值。如果是,我们将该 NUMBER 转换为字符串,否则我们使用 9 位数字重新解析并显示它。"
  • @MagnatLU:它实际上不能容纳 xxx.63。它可以容纳 xxx.625 - 但该值也是最接近 xxx.68 的值,因此即使对于“.6”部分,它也不是真的准确。阅读我编辑的答案,看看你是否同意更多。
  • 我认为 .NET 的 float 是 IEEE-754 的精确实现,其中浮点数具有由其指数和分数确定的精确值,并且每次计算都使用精确值(float 操作被 JITted 到协处理器指令)。只有当 CLI 使用自定义方法并删除最后一个重要且精确的位时,才会转换为stringCOMNumber
  • @MagnatLU:是的——请看我的最后一段。当位需要解释为重要时,包括转换为decimal
  • 好的。根据严格的浮点规则,您可以信任这些数字,但在某些操作期间实现选择不信任。这对于非科学计算是有意义的。反正我猜你的答案比我的更准确。
【解决方案2】:

这是设计使然。浮点数可以保存您的数字 [edit] 非常准确 [/edit],但为了转换目的,它会将其四舍五入到最接近的整数,因为只有少数可表示的浮点值在您的数字和整数之间(1056964.75 和 1056964.88)。请参阅 SSCLI 中的 COMNumber::FormatSingle 和 COMDecimal::InitSingle。

【讨论】:

  • 不,它不能精确地保存该值 - 否则转换为 double 将打印出 1056964.63 而不是 1056964.625。
  • 你是对的。但我的观点是,Yurik 的结果是由于转换期间的非 IEEE 处理,而不是由于表示中的实际精度损失。
猜你喜欢
  • 2014-02-07
  • 2021-10-19
  • 1970-01-01
  • 1970-01-01
  • 2022-11-19
  • 2014-03-01
  • 1970-01-01
  • 2015-12-28
  • 1970-01-01
相关资源
最近更新 更多