C# Float expression: strange behavior when casting the result float to int
我有以下简单的代码:
1 2 3 | int speed1 = (int)(6.2f * 10); float tmp = 6.2f * 10; int speed2 = (int)tmp; |
1 2 | speed1 = 61 speed2 = 62 |
我知道我可能应该使用数学。圆而不是铸造,但我想知道为什么值是不同的。
我查看了生成的字节码,但是除了存储和加载之外,操作码是相同的。
我还尝试了Java中的相同代码,我正确地获得了62和62。
有人能解释一下吗?
编辑:在实际代码中,它不是直接的6.2f*10,而是一个函数调用*一个常量。我有以下字节码:
对于
1 2 3 4 5 6 | 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 |
对于
1 2 3 4 5 6 7 8 | 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 |
我们可以看到操作数是浮动的,唯一的区别是
至于虚拟机,我尝试使用mono/win7、mono/macos和.net/windows,结果是一样的。
首先,我假设您知道,由于浮点舍入(它实际上是值61.9999809265137,当表示为
答案是,在
如果是
练习:解释以下操作顺序的结果。
1 2 3 | double d = 6.2f * 10; int tmp2 = (int)d; // evaluate tmp2 |
更新:如注释所述,由于第二个参数隐式转换为
实际问题是,编译器允许(但不需要)使用比形式类型精度更高的中介(第11.2.2节)。这就是您在不同系统上看到不同行为的原因:在表达式
在第二个示例中,对
描述
浮点数很少精确。
看看乔恩·斯基茨·埃多克斯(JonSkeets-EDOCX1)(25)的课。通过这个类,您可以真正地将浮点数的值可视化为字符串。
1 2 | DoubleConverter.ToExactString((6.2f * 10)) // output 61.9999980926513671875 |
更多信息
- 乔恩·斯基特的双重转换课程
- assert.areequal()与system.double混淆
- 每一个计算机科学家都应该知道什么是浮点运算
看看IL:
1 2 3 4 5 6 7 | IL_0000: ldc.i4.s 3D // speed1 = 61 IL_0002: stloc.0 IL_0003: ldc.r4 00 00 78 42 // tmp = 62.0f IL_0008: stloc.1 IL_0009: ldloc.1 IL_000A: conv.i4 IL_000B: stloc.2 |
编译器将编译时常量表达式减少到它们的常量值,我认为在将常量转换为
我编译并反汇编了这段代码(在win7/.net 4.0上)。我猜编译器将浮动常量表达式计算为double。
1 2 3 4 5 6 7 8 9 10 | int speed1 = (int)(6.2f * 10); mov dword ptr [rbp+8],3Dh //result is precalculated (61) float tmp = 6.2f * 10; movss xmm0,dword ptr [000004E8h] //precalculated (float format, xmm0=0x42780000 (62.0)) movss dword ptr [rbp+0Ch],xmm0 int speed2 = (int)tmp; cvttss2si eax,dword ptr [rbp+0Ch] //instrunction converts float to Int32 (eax=62) mov dword ptr [rbp+10h],eax |
我的猜测是,具有浮点精度的
编辑:根据评论,我将
1 | Int32 speed0 = (Int32)(6.2f * 100000000); |
给出61999980的结果,所以(Int32)(6.2f*10)给出61。
当两个单值相乘时是不同的,在这种情况下,没有截断操作,只有近似值。
请参阅http://msdn.microsoft.com/en-us/library/system.single.aspx
是否有原因将类型转换为
1 | int speed1 = (int)(6.2f * 10) |
然后阅读
1 | int speed1 = Int.Parse((6.2f * 10).ToString()); |
区别可能与四舍五入有关:如果您将其强制转换为
请注意以下输出
1 2 | int speed1 = (int)(6.2f * 10);//61 double speed2 = (6.2f * 10);//61.9999980926514 |
这就是为什么你得到不同的价值观!