【发布时间】:2009-03-30 11:05:47
【问题描述】:
考虑以下 C# 代码:
double result1 = 1.0 + 1.1 + 1.2;
double result2 = 1.2 + 1.0 + 1.1;
if (result1 == result2)
{
...
}
result1 应该总是等于 result2 对吧?问题是,它没有。结果 1 为 3.3,结果 2 为 3.3000000000000003。唯一的区别是常量的顺序。
我知道双精度数的实现方式可能会出现舍入问题。我知道如果我需要绝对精度,我可以使用小数。或者我可以在 if 语句中使用 Math.Round() 。我只是一个想了解 C# 编译器在做什么的书呆子。谁能告诉我?
编辑:
感谢迄今为止建议阅读浮点运算和/或谈论 CPU 如何处理双精度数的固有不准确性的所有人。但我觉得我的问题的主旨仍未得到解答。这是我没有正确措辞的错。让我这样说:
分解上面的代码,我预计会发生以下操作:
double r1 = 1.1 + 1.2;
double r2 = 1.0 + r1
double r3 = 1.0 + 1.1
double r4 = 1.2 + r3
让我们假设上面的每个添加都有一个舍入误差(编号为 e1..e4)。所以 r1 包含舍入误差 e1,r2 包含舍入误差 e1 + e2,r3 包含 e3,r4 包含 e3 + e4。
现在,我不知道舍入误差是如何发生的,但我预计 e1+e2 等于 e3+e4。显然不是,但这对我来说似乎是错误的。另一件事是,当我运行上面的代码时,我没有得到任何舍入错误。这就是让我觉得是 C# 编译器而不是 CPU 做一些奇怪的事情的原因。
我知道我问了很多问题,也许任何人都能给出的最佳答案是去攻读 CPU 设计博士学位,但我只是想我会问。
编辑 2
从我的原始代码示例中查看 IL,很明显是编译器而不是 CPU 执行此操作:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 1
.locals init (
[0] float64 result1,
[1] float64 result2)
L_0000: nop
L_0001: ldc.r8 3.3
L_000a: stloc.0
L_000b: ldc.r8 3.3000000000000003
L_0014: stloc.1
L_0015: ret
}
编译器正在帮我加起来!
【问题讨论】:
标签: c# .net compiler-construction rounding precision