在32位进程中进行类型转换后的值错误

时间:2014-06-19 10:07:54

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

请参阅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。

1 个答案:

答案 0 :(得分:3)

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

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

  

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

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

在这种情况下,我相信使用64位算法有效地执行除法,然后将double直接转换为int ,而不是通过{{ 1}}首先。

这里有一些代码来演示,使用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);
    }
}

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

这只是浮动二进制点算术的乐趣之一 - 以及为什么你需要非常小心你正在做什么的一个例子。

请注意,0.1f 正好 0.100000001490116119384765625 - 因此大于0.1。鉴于它超过0.1,我期望 100 99.999998509883909036943805404007434844970703125 100 100 99 100 略小于100 - 如果“少一点”可以表示,那么截断结果自然会导致99