铸造结果漂浮在方法返回浮动变化导致结果、方法

2023-09-02 01:27:16 作者:可爱炸了

为什么这code打印假的.NET 4 ?似乎有些意外的行为,正在引起显式类型转换。

Why does this code print False in .NET 4? It seems some unexpected behavior is being caused by the explicit cast.

我想答案超越浮点运算是不正确的或不这样做。

I'd like an answer beyond "floating point is inaccurate" or "don't do that".

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:此code来自一个单元测试,不会释放code 。 PPS:单元测试是通过在.NET 3.5,但是现在升级到.NET 4失败后

PS: This code came from a unit test, not release code. PPS: The unit test was passing in .NET 3.5, but now fails after the upgrade to .NET 4.

推荐答案

大卫的评论是正确的,但不够强。谁也不能保证,这样做是计算的两次在同一个程序的会产生相同的结果。

David's comment is correct but insufficiently strong. There is no guarantee that doing that calculation twice in the same program will produce the same results.

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

The C# specification is extremely clear on this point:

浮点运算可与更高precision比运算的结果类型来执行。例如,一些硬件结构支持的扩展或长双浮点更大范围和precision比double类型的类型,并隐式地使用这种更precision型全浮点运算。只有在性能开销过大,才能使这样的硬件结构用较少的precision执行浮点运算,而非需要一个执行没收性能和precision,C#允许一个更高的precision型用于所有浮点运算。除了提供更多的precise结果,这种情况很少会产生任何可察觉的影响。然而,在形式 EX pressions X * Y / Z ,如果其中的乘法产生的结果超出了双范围,而后面的除法使临时导致返回到double范围,事实上,前pression在更大范围的格式评价可能导致一个有限的结果要生产,而不是无穷大。

Floating-point operations may be performed with higher precision than the result type of the operation. For example, some hardware architectures support an "extended" or "long double" floating-point type with greater range and precision than the double type, and implicitly perform all floating-point operations using this higher precision type. Only at excessive cost in performance can such hardware architectures be made to perform floating-point operations with less precision, and rather than require an implementation to forfeit both performance and precision, C# allows a higher precision type to be used for all floating-point operations. Other than delivering more precise results, this rarely has any measurable effects. However, in expressions of the form x * y / z, where the multiplication produces a result that is outside the double range, but the subsequent division brings the temporary result back into the double range, the fact that the expression is evaluated in a higher range format may cause a finite result to be produced instead of an infinity.

C#编译器,抖动和运行都有着广泛的lattitude给你的更准确的结果的比被要求的规格,在任何时候,在一个突发奇想 - 他们不要求选择一贯如此做,其实他们不这样做。

The C# compiler, the jitter and the runtime all have broad lattitude to give you more accurate results than are required by the specification, at any time, at a whim -- they are not required to choose to do so consistently and in fact they do not.

如果你不喜欢的话就不要用二进制浮点数;无论是使用十进制或任意precision有理数。

If you don't like that then do not use binary floating point numbers; either use decimals or arbitrary precision rationals.

我不明白为什么投了返回浮动的方法浮动使得它的差异

全球最大漂浮式海上风电项目开工 受益上市公司梳理

I don't understand why casting to float in a method that returns float makes the difference it does

良好的出发点。

您的示例程序演示的微小变化如何导致很大的影响。你注意到,在一些版本的运行时,铸造明确漂浮给出了不同的结果比不这样做。 当你明确地转换为浮动,C#编译器给出了一个暗示,运行时说拿这个事情出了超高precision模式下,如果你碰巧使用这种优化。由于该规范指出,这有一个潜在的性能代价。

Your sample program demonstrates how small changes can cause large effects. You note that in some version of the runtime, casting to float explicitly gives a different result than not doing so. When you explicitly cast to float, the C# compiler gives a hint to the runtime to say "take this thing out of extra high precision mode if you happen to be using this optimization". As the specification notes, this has a potential performance cost.

,这样做正好一轮的正确答案仅仅是一个幸福的意外;得到,因为在这种情况下的损失precision发生失去它在正确的方向上的正确答案。

That doing so happens to round to the "right answer" is merely a happy accident; the right answer is obtained because in this case losing precision happened to lose it in the correct direction.

如何为.NET 4中不同?

How is .net 4 different?

您问的区别是什么,并在3.5 4.0的运行时间;所不同的是清晰,在4.0中,抖动选择去高precision在特定的情况下,和3.5抖动选择不。这并不意味着,这种情况在3.5是不可能的;它有可能在每一个版本的运行时,每一个版本的C#编译器的。你正好横跨在哪里,你的机器上,它们在细节上有所不同的情况下运行。但有抖动的总是的被允许做这样的优化,并且一直在其心血来潮这样做的。

You ask what the difference is between 3.5 and 4.0 runtimes; the difference is clearly that in 4.0, the jitter chooses to go to higher precision in your particular case, and the 3.5 jitter chooses not to. That does not mean that this situation was impossible in 3.5; it has been possible in every version of the runtime and every version of the C# compiler. You've just happened to run across a case where, on your machine, they differ in their details. But the jitter has always been allowed to make this optimization, and always has done so at its whim.

C#编译器也完全有权利选择计算在编译时间常数花车时做出类似的优化。两个貌似相同的计算在常量可以有不同的结果,这取决于编译器的运行时状态的详细信息。

The C# compiler is also completely within its rights to choose to make similar optimizations when computing constant floats at compile time. Two seemingly-identical calculations in constants may have different results depending upon details of the compiler's runtime state.

更一般地,你的期望,浮点数应该有实数的代数性质是完全不符合现实行;它们不具有那些代数性质。浮点运算,甚至没有的关联的;他们肯定你似乎他们期望不服从乘法逆规律。浮点数只有实数运算的近似;一个近似值是足够接近,比如说,模拟物理系统,或者计算汇总统计数据,或一些这样的事情。

More generally, your expectation that floating point numbers should have the algebraic properties of real numbers is completely out of line with reality; they do not have those algebraic properties. Floating point operations are not even associative; they certainly do not obey the laws of multiplicative inverses as you seem to expect them to. Floating point numbers are only an approximation of real arithmetic; an approximation that is close enough for, say, simulating a physical system, or computing summary statistics, or some such thing.