32位处理中类型转换后的值错误 [英] Wrong Value after type casting in 32 bit process

查看:158
本文介绍了32位处理中类型转换后的值错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请参阅C#中的以下代码。

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

float c = a / b;
int indirect =(int)(c);
//间接的值是100总是

int direct =(int)(a / b);
// 32位进程中的直接值为99($)
// 64位进程中直接值为100

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



我使用VS2013。

解决方案

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

从C#5规范的4.1.6节:


浮点运算以比操作的结果类型更高的精度来执行。例如,一些硬件架构支持具有比双重类型更大的范围和精度的扩展或长双浮点类型,并且使用此更高精度类型隐式地执行所有浮点运算。只有在性能成本过高的情况下,才能使这种硬件架构以较小的精度执行浮点运算,而不是要求实现同时丧失性能和精度,C#允许将更高精度的类型用于所有浮点运算。除了提供更精确的结果,这很少有任何可测量的影响。然而,在形式为x * y / z的表达式中,其中乘法产生在双范围之外的结果,但是随后的除法将临时结果带回到双范围中,表达式在更高的


我希望在某些优化方案中,它会产生一个有限的结果,如果JIT决定它从不真正需要值作为 float ,甚至可能的答案是错误与额外的局部变量。 (我看到的情况下,只是添加日志改变了这里的行为...)



在这种情况下,我相信这种划分是有效地使用64位算术,然后从 double 直接转换为 int ,而不是通过 float



这里有一些代码演示了使用 DoubleConverter 类,它允许您查找浮点二进制点数的精确十进制表示形式:

  ; 

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位。



这只是浮点二进制点运算的一个喜悦,也是为什么你需要非常小心你在做什么。 / p>

请注意,0.1f是完全 0.100000001490116119384765625 - 超过0.1。考虑到它大于0.1,我将 10 / b 小于100 - 如果少一点是可表示的,那么截断结果自然会导致99。


Please see the following code in 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

Why do we get 99 in 32-bit processes?

I am using VS2013.

解决方案

When you operate directly, it's permittable for operations to be performed at a higher precision, and for that higher precision to be continued for multiple operations.

From section 4.1.6 of the C# 5 specification:

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.

I'd expect that in some optimization scenarios, it would even be possible for the answer to be "wrong" with the extra local variable, if the JIT decides that it never really needs the value as a float. (I've seen cases where just adding logging changes the behaviour here...)

In this case, I believe that the division is effectively being performed using 64-bit arithmetic and then cast from double straight to int rather than going via float first.

Here's some code to demonstrate that, using a DoubleConverter class which allows you to find the exact decimal representation of a floating binary point number:

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);
    }
}

Output:

100
99.999998509883909036943805404007434844970703125
100
100
99
100

Note that the operation may not just be performed in 64-bits - it may be performed at even higher precision, e.g. 80 bits.

This is just one of the joys of floating binary point arithmetic - and an example of why you need to be very careful about what you're doing.

Note that 0.1f is exactly 0.100000001490116119384765625 - so more than 0.1. Given that it's more than 0.1, I would expect 10/b to be a little less than 100 - if that "little less" is representable, then truncating the result is going to naturally lead to 99.

这篇关于32位处理中类型转换后的值错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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