在返回浮点数的方法中将结果投入浮点运算结果

为什么这个代码在.NET 4中打印False ? 看起来有些意外的行为是由明确的演员造成的。

我想要一个超越“浮点不准确”或“不这样做”的答案。

 float a(float x, float y) { return ( x * y ); } float b(float x, float y) { return (float)( x * y ); } void Main() { Console.WriteLine( a( 10f, 1f/10f ) == b( 10f, 1f/10f ) ); } 

PS:这个代码来自一个unit testing,而不是发布代码。
PPS:unit testing在.NET 3.5中传递,但现在在升级到.NET 4后失败了。

大卫的评论是正确的,但不够强。 不能保证在同一个程序中进行两次计算会得到相同的结果。

C#规范在这一点上非常明确:


浮点运算可以以比操作的结果types更高的精度执行。 例如,某些硬件体系结构支持比doubletypes更宽的范围和精度的“extended”或“long double”浮点types,并隐式执行使用此更高精度types的所有浮点运算。 只有性能成本过高,才能使这种硬件体系结构以较低的精度执行浮点运算,而不是要求实现同时丧失性能和精度,C#允许更高精度types用于所有浮点运算。 除了提供更精确的结果之外,这很less有任何可衡量的效果。 然而,在forms为x * y / zexpression式中,乘法产生的结果在双重范围之外,但随后的分割将临时结果带回到双重范围内,则expression式以更高范围格式可能会导致产生有限的结果而不是无穷大。


C#编译器,抖动和运行时间都有很大的不同,给你比规范要求更准确的结果 ,在任何时候,随时都可以 – 他们不需要一致地select这样做,事实上他们做不。

如果你不喜欢,那么不要使用二进制浮点数; 要么使用小数或任意精度的理由。

我不明白为什么在一个返回浮点数的方法中浮点数会造成差异

优点。

您的示例程序演示了如何小的更改可以导致大的影响。 您注意到,在某些版本的运行时中,明确地转换为浮动会产生不同的结果。 当你明确强制转换为浮点运算时,C#编译器给运行时提示 “如果碰巧使用了这种优化,就把它从超高精度模式中拿出来”。 正如说明书所指出的那样, 这具有潜在的性能成本。

这样做恰好是“正确的答案”只是一个快乐的事故; 得到正确的答案是因为在这种情况下失去了精确度,正好在正确的方向上失去了它

.NET 4如何不同?

你问3.5和4.0运行时间有什么不同? 不同的是,在4.0中,抖动select在您的具体情况下select更高的精度,而3.5抖动select不会。 这并不意味着这种情况在3.5中是不可能的; 在运行时的每个版本和C#编译器的每个版本中都是可能的。 你碰巧碰到一个情况,在你的机器上,他们的细节有所不同。 但是抖动总是被允许进行这种优化,而且总是这样做。

在编译时计算常量浮点数时,C#编译器也完全有权select进行类似的优化。 常量中的两个看似相同的计算可能会有不同的结果,具体取决于编译器运行时状态的细节。

更一般地说,你期望浮点数应该具有实数的代数性质,完全不符合实际; 他们没有这些代数性质。 浮点运算甚至没有关联 ; 他们当然不会像你期望的那样服从乘法反演的规律。 浮点数只是实数算术的近似值; 一个近似值足够接近,比如模拟一个物理系统,或者计算摘要统计量,或者一些这样的事情。

我现在没有Microsoft编译器,Mono没有这样的效果。 据我所知, GCC 4.3+在编译时使用gmp和mpfr来计算一些东西 。 C#编译器可以为相同程序集中的非虚拟,静态或私有方法执行相同的操作。 显式强制转换可能会干扰这种优化(但我没有看到为什么它不能有相同的行为)。 也就是说,它可以通过计算常量expression式来达到某种程度(对于b()它可以是例如直到演员)。

如果有意义的话,GCC还有对更高的精度提升操作的优化。

所以我会考虑优化作为潜在的原因。 但是对于他们两个人来说,我看不出为什么明确expression结果可能会有一些额外的含义,如“接近标准”。