首页 > 解决方案 > 将 float 转换为 int、32 位 C 的区别

问题描述

我目前正在使用需要运行 32 位系统的旧代码。在这项工作中,我偶然发现了一个(出于学术兴趣)我想了解其原因的问题。

如果在变量或表达式上进行强制转换,那么在 32 位 C 中从 float 转换为 int 的行为似乎有所不同。考虑程序:

#include <stdio.h>
int main() {
   int i,c1,c2;
   float f1,f10;
   for (i=0; i< 21; i++)  {
      f1 = 3+i*0.1;
      f10 = f1*10.0;
      c1 = (int)f10;
      c2 = (int)(f1*10.0);
      printf("%d, %d, %d, %11.9f, %11.9f\n",c1,c2,c1-c2,f10,f1*10.0);
   }
}

使用修饰符直接在 32 位系统或 64 位系统上编译(使用 gcc)-m32,程序的输出为:

30, 30, 0, 30.000000000 30.000000000
31, 30, 1, 31.000000000 30.999999046
32, 32, 0, 32.000000000 32.000000477
33, 32, 1, 33.000000000 32.999999523
34, 34, 0, 34.000000000 34.000000954
35, 35, 0, 35.000000000 35.000000000
36, 35, 1, 36.000000000 35.999999046
37, 37, 0, 37.000000000 37.000000477
38, 37, 1, 38.000000000 37.999999523
39, 39, 0, 39.000000000 39.000000954
40, 40, 0, 40.000000000 40.000000000
41, 40, 1, 41.000000000 40.999999046
42, 41, 1, 42.000000000 41.999998093
43, 43, 0, 43.000000000 43.000001907
44, 44, 0, 44.000000000 44.000000954
45, 45, 0, 45.000000000 45.000000000
46, 45, 1, 46.000000000 45.999999046
47, 46, 1, 47.000000000 46.999998093
48, 48, 0, 48.000000000 48.000001907
49, 49, 0, 49.000000000 49.000000954
50, 50, 0, 50.000000000 50.000000000 

因此,很明显,转换变量和表达式之间存在差异。请注意,如果float更改为double和/或int更改为shortor long,问题也存在,如果程序编译为 64 位,问题也不会出现。

为了澄清,我在这里试图理解的问题不是浮点算术/舍入,而是 32 位内存处理的差异。

该问题在以下方面进行了测试:

任何帮助我理解这里发生的事情的指针都值得赞赏。

标签: ccasting32-bit

解决方案


使用 MS Visual C 2008,我能够重现这一点。

检查汇编器,两者之间的区别在于中间存储和获取具有中间转换的结果:

  f10 = f1*10.0;          // double result f10 converted to float and stored
  c1 = (int)f10;          // float result f10 fetched and converted to double
  c2 = (int)(f1*10.0);    // no store/fetch/convert

生成的汇编器将值推送到 FPU 堆栈,这些值被转换为 64 位,然后相乘。c1然后将结果转换回浮点数并存储,然后再次检索并放置在 FPU 堆栈上(并再次转换为双精度数)以调用,这是一个将双精度__ftol2_sse数转换为 int 的运行时函数。

因为c2中间值不会与浮点数相互转换并立即传递给__ftol2_sse函数。对于此函数,另请参阅将 double 转换为 int 的答案?.

汇编器:

      f10 = f1*10;
fld         dword ptr [f1] 
fmul        qword ptr [__real@4024000000000000 (496190h)] 
fstp        dword ptr [f10] 

      c2 = (int)(f1*10);
fld         dword ptr [f1] 
fmul        qword ptr [__real@4024000000000000 (496190h)] 
call        __ftol2_sse
mov         dword ptr [c2],eax 

      c1 = (int)f10;
fld         dword ptr [f10] 
call        __ftol2_sse
mov         dword ptr [c1],eax 

推荐阅读