问题描述
我有以下简单的代码:
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 而不是强制转换,但我想了解为什么这些值不同.
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
比隐式转换为 更好 到 双
.
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
.
实际问题是编译器被允许(但不是必需)使用 比正式类型的精度更高(第 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 表达式:将结果浮点数转换为 int 时的奇怪行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!