从int转换为float的问题

时间:2009-06-19 14:25:30

标签: c# floating-point

有一种我无法理解的奇怪行为。 同意浮点数是近似值,因此即使显示返回没有十进制数的数字的操作也可以近似为带小数的数字。

我这样做:

int num = (int)(195.95F * 100);

由于它是一个浮点运算,我得到19594而不是19595 ..但这是正确的。

让我感到困惑的是,如果我做了

float flo = 195.95F * 100;
int num = (int) flo;

我得到19595的正确结果。

知道为什么会这样吗?

6 个答案:

答案 0 :(得分:11)

我看看这是否是编译器正在进行数学计算,但即使你强制执行它也会采用这种方式:

static void Main()
{
    int i = (int)(GetF() * GetI()); // 19594
    float f = GetF() * GetI();
    int j = (int)f; // 19595
}
[MethodImpl(MethodImplOptions.NoInlining)]
static int GetI() { return 100; }
[MethodImpl(MethodImplOptions.NoInlining)]
static float GetF() { return 195.95F; }

看起来差别在于它是否保留在寄存器中(比正常的r4宽)或被强制为float变量:

L_0001: call float32 Program::GetF()
L_0006: call int32 Program::GetI()
L_000b: conv.r4 
L_000c: mul 
L_000d: conv.i4 
L_000e: stloc.0 

VS

L_000f: call float32 Program::GetF()
L_0014: call int32 Program::GetI()
L_0019: conv.r4 
L_001a: mul 
L_001b: stloc.1 
L_001c: ldloc.1 
L_001d: conv.i4 
L_001e: stloc.2 

唯一的区别是stloc.1 / ldloc.1

这得到以下事实的支持:如果你进行优化构建(将删除局部变量),我会得到相同的答案(19594)。

答案 1 :(得分:2)

此代码......

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            float result = 195.95F*100;
            int intresult = (int)(195.95F * 100);
        }
    }
}

给这个IL

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       14 (0xe)
  .maxstack  1
  .locals init ([0] float32 result,
           [1] int32 intresult)
  IL_0000:  nop
  IL_0001:  ldc.r4     19595.
  IL_0006:  stloc.0
  IL_0007:  ldc.i4     0x4c8a
  IL_000c:  stloc.1
  IL_000d:  ret
} // end of method Program::Main

看看IL_00001 - > compier完成了计算.. 否则有小数 - >二进制转换问题

答案 2 :(得分:2)

Mark的回答是正确的,因为它是nativefloat和float32 / float64之间的转换。

CLR ECMA规范对此进行了介绍,但David Notario对此的解释远远超出了我的预测。

答案 3 :(得分:1)

在第二个例子中尝试将float转换为double:

double flo = 195.95F * 100;
int num = (int) flo;

我猜你的第一个例子是编译器使用double来保存中间结果,所以在float情况下你会失去精度。

答案 4 :(得分:0)

当你乘以100时,这是一个整数,所以它在该步骤进行隐式转换。如果你把“F”放在100后面,我敢打赌他们会是一样的。

当它是引用类型时,我通常使用括号的装箱/拆箱。当它是值类型时,我尝试使用Convert static methods。

尝试Convert.ToSingle(YourNumber);为了更可靠的转换。

HTH

答案 5 :(得分:0)

我无法回答为什么第二个有效,第一个没有。但是,我可以告诉你,195.95是二进制中的非终止十进制数,因此像这样的舍入错误必然会发生。

尝试转换为double而不是float。您也可以使用金钱或小数类型而不是浮点数。这将以不同且更准确的方式存储数字。

有关浮点数和IEEE表示的更多信息,请转到此处:

http://en.wikipedia.org/wiki/IEEE_754