C#Float表达式:将结果float转换为int时的奇怪行为 [英] C# Float expression: strange behavior when casting the result float to int

查看:94
本文介绍了C#Float表达式:将结果float转换为int时的奇怪行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下简单代码:

int speed1 = (int)(6.2f * 10);
float tmp = 6.2f * 10;
int speed2 = (int)tmp;

speed1 speed2 应该具有相同的值,但实际上,我有:

speed1 and speed2 should have the same value, but in fact, I have :

speed1 = 61
speed2 = 62

我知道我应该使用Math.Round而不是强制转换,但是我d想了解为什么值会不同。

I know I should probably use Math.Round instead of casting, but I'd like to understand why the values are different.

我查看了生成的字节码,但是除了存储和加载外,操作码是相同的。

I looked at the generated bytecode, but except a store and a load, the opcodes are the same.

我也在Java中尝试了相同的代码,并且我正确地获得了62和62。

I also tried the same code in java, and I correctly obtain 62 and 62.

有人可以解释吗?

编辑:
在实际代码中,它不是直接6.2f * 10,而是函数调用*常量。我有以下字节码:

Edit : In the real code, it's not directly 6.2f * 10 but a function call * a constant. I have the following bytecode :

用于 speed1

IL_01b3:  ldloc.s    V_8
IL_01b5:  callvirt   instance float32 myPackage.MyClass::getSpeed()
IL_01ba:  ldc.r4     10.
IL_01bf:  mul
IL_01c0:  conv.i4
IL_01c1:  stloc.s    V_9

对于 speed2

IL_01c3:  ldloc.s    V_8
IL_01c5:  callvirt   instance float32 myPackage.MyClass::getSpeed()
IL_01ca:  ldc.r4     10.
IL_01cf:  mul
IL_01d0:  stloc.s    V_10
IL_01d2:  ldloc.s    V_10
IL_01d4:  conv.i4
IL_01d5:  stloc.s    V_11

我们可以看到操作数是浮点数,唯一的区别是 stloc / ldloc

we can see that operands are floats and that the only difference is the stloc/ldloc.

对于虚拟机,我尝试在Mono / Win7,Mono / MacOS和.NET / Windows上使用,结果相同。

As for the virtual machine, I tried with Mono/Win7, Mono/MacOS, and .NET/Windows, with the same results.

推荐答案

首先所有,我假设您知道 6.2f * 10 由于浮点舍入而不是精确地为62(当表示为 double 时,其实际值为61.99999809265137),并且您的问题仅在于为什么两个看似相同的计算会导致错误的值。

First of all, I assume that you know that 6.2f * 10 is not exactly 62 due to floating point rounding (it's actually the value 61.99999809265137 when expressed as a double) and that your question is only about why two seemingly identical computations result in the wrong value.

答案是在(int)(6.2f * 10),您将 double 值61.99999809265137取整并将其截断为整数,得出61。

The answer is that in the case of (int)(6.2f * 10), you are taking the double value 61.99999809265137 and truncating it to an integer, which yields 61.

对于 float f = 6.2f * 10 的情况,您将双精度值61.99999809265137和取整应用于最接近的 float ,即62。然后将 float 截断为整数,结果为62。

In the case of float f = 6.2f * 10, you are taking the double value 61.99999809265137 and rounding to the nearest float, which is 62. You then truncate that float to an integer, and the result is 62.

锻炼:解释以下操作顺序的结果。

Exercise: Explain the results of the following sequence of operations.

double d = 6.2f * 10;
int tmp2 = (int)d;
// evaluate tmp2

更新:如注释中所述,表达式 6.2f * 10 在形式上是 float ,因为第二个参数隐式转换为 float 比隐式转换为更好 code> double 。

Update: As noted in the comments, the expression 6.2f * 10 is formally a float since the second parameter has an implicit conversion to float which is better than the implicit conversion to double.

实际的问题是允许(但不是必需)编译器使用< a href = https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-334.pdf rel = noreferrer>比正式类型的精度更高(11.2.2节)。这就是为什么您在不同的系统上看到不同的行为的原因:在表达式(int)(6.2f * 10)中,编译器可以选择将值保留为 6.2f * 10 以高精度中间形式转换为 int 之前。如果是,则结果为61。如果不是,则结果为62。

The actual issue is that the compiler is permitted (but not required) to use an intermediate which is higher precision than the formal type (section 11.2.2). That's why you see different behavior on different systems: In the expression (int)(6.2f * 10), the compiler has the option of keeping the value 6.2f * 10 in a high precision intermediate form before converting to int. If it does, then the result is 61. If it does not, then the result is 62.

在第二个示例中,显式分配给 float 强制四舍五入在转换为整数之前进行。

In the second example, the explicit assignment to float forces the rounding to take place before the conversion to integer.

这篇关于C#Float表达式:将结果float转换为int时的奇怪行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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