【问题标题】:Wrong Value after type casting in 32 bit process [duplicate]在 32 位进程中类型转换后的错误值 [重复]
【发布时间】:2014-06-19 10:07:54
【问题描述】:

请看下面的 C# 代码。

        float a = 10.0f;
        float b = 0.1f;

        float c = a / b;
        int indirect = (int)(c);
        // Value of indirect is 100 always

        int direct = (int)(a / b);
        // Value of direct is 99 in 32 bit process (?)
        // Value of direct is 100 in 64 bit process

为什么我们在 32 位进程中得到 99?

我正在使用 VS2013。

【问题讨论】:

    标签: c# casting floating-point 32bit-64bit


    【解决方案1】:

    当您直接操作时,允许以更高的精度执行操作,并且允许多个操作继续以更高的精度。

    来自 C# 5 规范的第 4.1.6 节:

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

    我希望在某些优化方案中,如果 JIT 决定它永远不需要将值作为 float ,那么额外局部变量的答案甚至可能是“错误的”。 (我已经看到仅添加日志记录会改变此处行为的情况......)

    在这种情况下,我认为除法实际上是使用 64 位算术执行的,然后将 double 直接转换为 int,而不是先通过 float

    这里有一些代码来证明这一点,使用DoubleConverter 类,它可以让您找到浮点数的精确十进制表示:

    using System;
    
    class Test
    {
        static void Main()
        {
            float a = 10f;
            float b = 0.1f;
            float c = a / b;
            double d = (double) a / (double) b;
            float e = (float) d;
            Console.WriteLine(DoubleConverter.ToExactString(c));
            Console.WriteLine(DoubleConverter.ToExactString(d));
            Console.WriteLine(DoubleConverter.ToExactString(e));
            Console.WriteLine((int) c);
            Console.WriteLine((int) d);
            Console.WriteLine((int) e);
        }
    }
    

    输出:

    100
    99.999998509883909036943805404007434844970703125
    100
    100
    99
    100
    

    请注意,该操作可能不仅仅以 64 位执行 - 它可能以更高的精度执行,例如80 位。

    这只是浮点二进制运算的乐趣之一 - 也是为什么您需要非常小心您正在做的事情的一个例子。

    请注意,0.1f 正好 0.100000001490116119384765625 - 所以大于 0.1。鉴于它大于 0.1,我 预计 10/b 会略小于 100 - 如果“略少”是可表示的,那么截断结果自然会导致 99。

    【讨论】:

    • 从记忆中,标准说“这是特定于实现的” - 因此精度可能因平台而异。再一次 - 一个令人难以置信的答案,如此之快。 +1
    • @SimonWhitehead:是的 - 添加了规范参考以获得更多解释。
    • 这不是“浮点数运算的乐趣”。这是 1) 未充分说明的编程语言定义和 2) 实现依赖于 12 年前已经过时的 ABI 和指令集的乐趣所在。不必对定义进行详细说明(即 C99、Java),而且由于 SSE2 于 2001 年推出,现在可以很容易地开始使用它。
    • @Jon:感谢您及时详细的回复。 :)
    猜你喜欢
    • 1970-01-01
    • 2012-09-25
    • 2013-12-24
    • 1970-01-01
    • 2014-08-10
    • 2012-02-10
    • 2016-05-07
    • 2015-02-14
    • 1970-01-01
    相关资源
    最近更新 更多