将结果转换为浮点数的方法返回浮点数更改结果 [英] Casting a result to float in method returning float changes result

查看:24
本文介绍了将结果转换为浮点数的方法返回浮点数更改结果的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为什么此代码在 .NET 4 中打印 False?显式转换似乎导致了一些意外行为.

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:此代码来自单元测试,而不是发布代码.代码是故意这样写的.我怀疑它最终会失败,但我想知道确切的时间和确切原因.答案证明了这种技术的有效性,因为它提供的理解超出了对浮点确定性的通常理解.这就是以这种方式编写代码的目的;深思熟虑的探索.

PS: This code came from a unit test, not release code. The code was written this way deliberately. I suspected it would fail eventually but I wanted to know exactly when and exactly why. The answer proves the validity of this technique because it provides an understanding that goes beyond the usual understanding of floating point determinism. And that was the point of writing this code this way; deliberate exploration.

PPS:单元测试在 .NET 3.5 中通过,但现在升级到 .NET 4 后失败.

PPS: The unit test was passing in .NET 3.5, but now fails after the upgrade to .NET 4.

推荐答案

David 的评论是正确的,但不够有力.无法保证在同一程序中执行该计算两次会产生相同的结果.

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:

浮点运算可以以比运算结果类型更高的精度执行.例如,一些硬件架构支持比 double 类型具有更大范围和精度的扩展"或long double"浮点类型,并使用这种更高精度类型隐式执行所有浮点运算.只有在性能成本过高的情况下,才能使此类硬件架构以较低的精度执行浮点运算,而不是要求实现同时丧失性能和精度,C# 允许将更高精度的类型用于所有浮点运算.除了提供更精确的结果之外,这很少有任何可衡量的影响.但是,在 x * y/z 形式的表达式中,乘法产生的结果超出双精度范围,但随后的除法将临时结果带回双精度范围,事实是以更高范围的格式计算表达式可能会导致产生有限结果而不是无穷大.

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# 编译器、抖动和运行时都具有广泛的自由度,可以在任何时候随心所欲地为您提供比规范要求的更准确的结果——它们不需要选择始终如一地这样做,事实上他们没有.


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.

如果您不喜欢这样,请不要使用二进制浮点数;使用小数或任意精度有理数.

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

我不明白为什么在返回 float 的方法中强制转换为 float 会有所不同

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

好点.

您的示例程序演示了微小的变化如何产生巨大的影响.您注意到,在某些版本的运行时中,显式转换为 float 会产生与不这样做不同的结果.当您显式转换为浮点数时,C# 编译器会提示运行时说如果您碰巧使用此优化,请将此事物退出超高精度模式".正如规范所指出的,这有潜在的性能成本.

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.

这样做碰巧得到了正确答案",这只是一个快乐的意外;获得了正确答案,因为在这种情况下失去精度恰好会在正确的方向上失去它.

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 中,抖动选择在您的特定情况下达到更高的精度,而 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.

这篇关于将结果转换为浮点数的方法返回浮点数更改结果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆