C# 中的双倍比较精度损失,加减双精度时发生精度损失
Posted
技术标签:
【中文标题】C# 中的双倍比较精度损失,加减双精度时发生精度损失【英文标题】:Double comparison precision loss in C#, accuracy loss happening when adding subtracting doubles 【发布时间】:2016-12-19 12:07:22 【问题描述】:刚开始学习 C#。我计划将它用于繁重的数学模拟,包括数值求解。问题是在添加和减去double
以及比较时会出现精度损失。代码及其返回的内容(以 cmets 为单位)如下:
namespace ex3
class Program
static void Main(string[] args)
double x = 1e-20, foo = 4.0;
Console.WriteLine((x + foo)); // prints 4
Console.WriteLine((x - foo)); // prints -4
Console.WriteLine((x + foo)==foo); // prints True BUT THIS IS FALSE!!!
不胜感激任何帮助和澄清!
让我困惑的是(x + foo)==foo
返回True
。
【问题讨论】:
请记住,不建议使用==
运算符比较浮点数。请参阅this post 了解如何进行操作。
@Marcelo 谢谢!您能否发布答案中的链接(比较浮点数的功能)?我觉得它很有用。
我的评论里也有。
另外看看十进制类型 - 范围更小,但比双精度更好:msdn.microsoft.com/en-us/library/364x0z75.aspx
【参考方案1】:
查看 double
的 MSDN 参考:https://msdn.microsoft.com/en-AU/library/678hzkk9.aspx
它指出double
的精度为 15 到 16 位。
但就位数而言,1e-20
和 4.0
之间的差异是 20 位数。仅仅尝试在4.0
中添加或减去1e-20
就意味着1e-20
丢失了,因为它不能满足15 到16 位的精度。
所以,就double
而言,4.0 + 1e-20 == 4.0
和4.0 - 1e-20 == 4.0
。
【讨论】:
那么为什么在 OP 的示例中(x + foo) == foo
为假?
@xr280xr - 不是。 OP 说// prints True BUT THIS IS FALSE!!!
意味着代码(x + foo) == foo
返回True
,但在数学上它应该是False
【参考方案2】:
对 Enigmativity 的回答的补充:
要使其工作,您需要更高的精度,并且 decimal
的精度为 28 到 29 位,基数为 10:
decimal x = 1e-20m, foo = 4.0m;
Console.WriteLine((x + foo)); // prints 4.00000000000000000001
Console.WriteLine((x - foo)); // prints -3.99999999999999999999
Console.WriteLine((x + foo) == foo); // prints false.
但请注意,小数确实具有较大的精度,但具有较低的范围。查看更多关于十进制的信息here
【讨论】:
【参考方案3】:您正在寻找的可能是十进制结构 (https://msdn.microsoft.com/en-us/library/system.decimal.aspx)。
Doubles
不能以您正在寻找的精度正确表示那种值 (C# double to decimal precision loss)。相反,请尝试使用 Decimal
类,如下所示:
decimal x = 1e-20M, foo = 4.0M;
Console.WriteLine(Decimal.Add(x, foo)); //prints 4,0000000000000000001
Console.WriteLine(Decimal.Add(x, -foo)); //prints -3,9999999999999999999
Console.WriteLine(Decimal.Add(x, foo) == foo); // prints false
【讨论】:
【参考方案4】:这不是 C# 的问题,而是您的计算机的问题。它并不是很复杂或难以理解,但它是一个很长的阅读。如果您想深入了解计算机的工作原理,您应该阅读this article。
一个优秀的 TLDR 网站 imho 是比上述文章更好的主题介绍是这个:
http://floating-point-gui.de/
我将为您提供一个非常简短的说明,说明发生了什么,但您当然应该至少阅读该网站,以避免将来出现麻烦,因为您的应用领域需要深入了解这些知识。
会发生以下情况:您有 1e-20,它比 1.11e-16 小。另一个数字被称为计算机上的双精度机器 epsilon(很可能)。如果您将等于或大于 1 的数字添加到小于机器 epsilon 的值,它将被四舍五入,返回大数。这是由于 IEEE 754 表示。这意味着在加法发生后,结果是“正确的”(就好像你有无限精度),结果以有限/有限精度的格式存储,将 4.00....001 舍入到 4,因为取整误差小于1.11e-16,可以接受。
【讨论】:
感谢您的详尽解释以及您的宝贵时间。希望可以接受 2 个答案。 这与计算机本身没有任何关系。 100% 与 C#(和 .NET)以及框架设计者选择的浮点数表示有关。 另外,您不应在外部链接中提供答案。您应该将内容放在答案中,并且仅提供支持您所写内容的链接。 每台具有 64 位 fp 算法的计算机的行为都是相同的。这不是 C#,而是 IEEE 754 的工作方式。如果他们以不同的方式实现双精度,那么它们实际上就不会是每种语言都称为双精度浮点数 我确实回答了这个问题,只是提供了进一步的阅读,因为它对于一个 QA 网站来说太长了,而且是必不可少的 imo。以上是关于C# 中的双倍比较精度损失,加减双精度时发生精度损失的主要内容,如果未能解决你的问题,请参考以下文章