浮点variables的范围会影响它们的值吗?
如果我们在控制台应用程序上执行下面的C#代码,我们会得到一条消息, The sums are Not equal
。
如果我们在取消注释System.Console.WriteLine()
之后执行它,我们将得到一条消息,因为The sums are equal
。
static void Main(string[] args) { float f = Sum(0.1f, 0.2f); float g = Sum(0.1f, 0.2f); //System.Console.WriteLine("f = " + f + " and g = " + g); if (f == g) { System.Console.WriteLine("The sums are equal"); } else { System.Console.WriteLine("The sums are Not equal"); } } static float Sum(float a, float b) { System.Console.WriteLine(a + b); return a + b; }
这种行为的实际原因是什么?
这与范围无关。 这是堆栈dynamic和浮点处理的结合。 编译器的一些知识将有助于使这个直觉行为清楚。
当Console.WriteLine
被注释时,值f
和g
在评估堆栈上并保持在那里,直到在Main方法中通过相等性testing。
当没有对Console.Writeline
进行注释时,值f
和g
将在调用时从评估堆栈移到调用堆栈,并在Console.WriteLine
返回时恢复到评估堆栈。 而你的比较if (f == g)
是事后完成的。 在将值存储到调用堆栈期间可能会发生一些舍入,并且可能会丢失一些信息。
在调用Console.WriteLine
的场景中,比较testing中的f
和g
不是相同的值。 它们已被复制并恢复为虚拟机具有不同精度和舍入规则的格式。
在您的特定代码中,当对Console.WriteLine
的调用进行注释时,评估堆栈不会存储到调用堆栈,也不会发生舍入。 由于平台的实现允许提高评估堆栈的精度,因此可能会出现这种差异。
编辑我们在这种情况下击中的是CLI规范允许的。 在第I.12.1.3节中,其内容如下:
浮点数(静态,数组元素和类的字段)的存储位置的大小是固定的。 支持的存储大小是float32和float64。 其他地方(在评估堆栈上,作为参数,返回types和本地variables)使用内部浮点types来表示浮点数。 在每个这样的例子中,variables或expression式的名义types是float32或float64,但是它的值可以用额外的范围和/或精度在内部表示。 内部浮点表示的大小是依赖于实现的,可以改变,并且至less应该与所表示的variables或expression式一样大。
这个引用的关键字是“依赖于实现”和“可以变化”。 在OP的情况下,我们看到他的实施确实有所不同。
Java平台中的non-strictfp浮点运算也有一个相关的问题,关于更多的信息请查看我对JVM上的浮点操作会在所有平台上得到相同结果的答案吗?
这种行为的实际原因是什么?
我无法提供具体情况下的具体细节,但是我了解一般问题,以及为什么使用Console.WriteLine
可以改变事情。
正如我们在之前的文章中所看到的 ,有时在浮点types上执行的操作的精度要高于在variablestypes中指定的精度。 对于局部variables,可以包括在执行方法期间如何将值存储在内存中。
我怀疑你的情况:
-
Sum
方法正在内联(但稍后会看到) - 总和本身比你所期望的32位浮点更精确
- 其中一个variables(
f
表示)的值被存储在一个高精度的寄存器中- 对于这个variables,“更精确”的结果是直接存储的
- 另一个variables(
g
)的值作为一个32位值存储在堆栈中- 对于这个variables,“更精确”的结果正在减less到32位
- 当进行比较时,堆栈上的variables被提升为更高精度的值,并与其他更高精度的值进行比较,差异是由于其中一个先前已经丢失了信息而另一个没有
当你取消注释Console.WriteLine
语句时,我猜测(无论出于何种原因)强制这两个variables以“正确的”32位精度进行存储,所以它们都以相同的方式处理。
这个假设是有点混乱的事实,增加
[MethodImpl(MethodImplOptions.NoInlining)]
…并没有改变我看到的结果。 尽pipe如此,我可能还在做其他的事情。
真的,我们应该看看正在执行的汇编代码 – 不幸的是,现在我没有时间这么做。
(不是一个真正的答案,但希望有一些支持文件)
configuration:Core i7,Windows 8.1,Visual Studio 2013
平台x86:
Version Optimized Code? Debugger Enabled? Outcome 4.5.1 Yes No Not equal 4.5.1 Yes Yes Equal 4.5.1 No No Equal 4.5.1 No Yes Equal 2.0 Yes No Not Equal 2.0 Yes Yes Equal 2.0 No No Equal 2.0 No Yes Equal
平台x64:
Version Optimized Code? Debugger Enabled? Outcome 4.5.1 Yes No Equal 4.5.1 Yes Yes Equal 4.5.1 No No Equal 4.5.1 No Yes Equal 2.0 Yes No Equal 2.0 Yes Yes Equal 2.0 No No Equal 2.0 No Yes Equal
这种情况似乎只发生在x86configuration的优化代码上。