将 float 转换为 int 的区别,32 位 C
Difference in casting float to int, 32-bit 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 位系统上使用 -m32
修饰符编译(使用 gcc)程序的输出是:
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
and/or int
更改为 short
或 long
,该问题也存在如果程序编译为 64 位,则不会显示。
澄清一下,我在这里试图了解的问题不是关于浮点 arithmetic/rounding,而是 32 位内存处理的差异。
该问题已在以下平台上进行测试:
Linux 版本 4.15.0-45-generic (buildd@lgw01-amd64-031) (gcc 版本 7.3.0 (Ubuntu 7.3.0-16ubuntu3) ), 程序编译使用: gcc -m32 Cast32int.c
Linux 版本 2.4.20-8 (bhcompile@porky.devel.redhat.com) (gcc 版本 3.2.2 20030222 (Red Hat Linux 3.2.2-5 )), 程序编译使用: gcc Cast32int.c
感谢任何帮助我理解这里发生的事情的指示。
使用 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
汇编器生成的值将被转换为 64 位的 FPU 堆栈压入,然后相乘。对于 c1
,结果随后被转换回浮点数并存储,然后再次检索并放置在 FPU 堆栈中(并再次转换为双精度数)以调用 __ftol2_sse
、运行 -time 函数将 double 转换为 int。
对于c2
,中间值不是 与float 之间的转换,并立即传递给__ftol2_sse
函数。对于此功能,另请参阅 Convert double to 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
C 标准对于如何执行浮点运算不是很严格。该标准允许实现以比涉及的类型更高的精度进行计算。
您的结果很可能来自 c1
计算为 "float-to-int" 而 c2
计算为 "double-to-int"(或更高的精度) ).
这是显示相同行为的另一个示例。
#define DD 0.11111111
int main()
{
int i = 27;
int c1,c2,c3;
float f1;
double d1;
printf("%.60f\n", DD);
f1 = i * DD;
d1 = i * DD;
c1 = (int)f1;
c2 = (int)(i * DD);
c3 = (int)d1;
printf("----------------------\n");
printf("f1: %.60f\n", f1);
printf("d1: %.60f\n", d1);
printf("m : %.60f\n", i * DD);
printf("%d, %d, %d\n",c1,c2,c3);
}
我的输出:
0.111111109999999999042863407794357044622302055358886718750000
----------------------
f1: 3.000000000000000000000000000000000000000000000000000000000000
d1: 2.999999970000000182324129127664491534233093261718750000000000
m : 2.999999970000000182324129127664491534233093261718750000000000
3, 2, 2
这里的技巧是 0.11111111
中的个数。准确的结果是“2.99999997”。当您更改个数时,准确结果仍为“2.99...997”形式(即,当 1 的数量增加时,9 的数量增加)。
在某个点(也就是一些点)你会到达一个点,将结果存储在一个浮点数中,将结果四舍五入为“3.0”,而双精度仍然能够保持“2.999999 .....” .然后转换为 int 将给出不同的结果。
进一步增加 1 的数量将导致 double 也将四舍五入为“3.0”,因此转换为 int 将产生相同的结果。
在“32位系统”中,差异是由于f1*10.0
使用全double
精度,而f10
只有float
精度因为那是它的类型。 f1*10.0
使用 double
精度,因为 10.0
是一个 double
常量。当 f1*10.0
赋值给 f10
时,值发生变化,因为它被隐式转换为精度较低的 float
。
如果您改用 float
常量 10.0f
,差异就会消失。
考虑第一种情况,当i
为1时,则:
- 在
f1 = 3+i*0.1
中,0.1
是一个double
常数,所以在double
中进行运算,结果为3.100000000000000088817841970012523233890533447265625。然后,将其分配给 f1
,它被转换为 float
,产生 3.099999904632568359375.
- 在
f10 = f1*10.0;
中,10.0
是一个double
常数,所以在double
中再次进行运算,结果为30.99999904632568359375。对于赋值给f10
,这个被转换为float
,结果是31.
- 后面打印
f10
和f1*10.0
时,我们看到上面给出的值,小数点后九位,f10
为“31.000000000”,“30.999999046” ”.
如果您打印 f1*10.0f
,使用 float
常量 10.0f
而不是 double
常量 10.0
,结果将是“31.000000000”而不是比“30.999999046”。
(以上使用IEEE-754基本32位和64位二进制浮点运算。)
特别注意:f1*10.0
和 f10
之间的区别出现在 f1*10.0
转换为 float
以分配给 f10
时。虽然 C 允许实现在评估表达式时使用额外的精度,但它要求实现在赋值和强制转换中放弃这种精度。因此,在符合标准的编译器中,对 f10
的赋值必须 使用 float
精度。这意味着,即使程序是为“64 位系统”编译的,差异 也应该 出现。否则,编译器不符合 C 标准。
此外,如果将float
更改为double
,则不会发生向float
的转换,并且不会更改值。在这种情况下,f1*10.0
和 f10
之间应该没有区别。
鉴于问题报告的差异在“64 位”编译中没有体现出来,而在 double
中体现出来,是否准确报告观察结果值得怀疑。为澄清这一点,应显示确切的代码,并应由第三方复制观察结果。
主要原因是后面两行the rounding-control (RC) field of the x87 FPU control register
值不一致。最终c1和c2的值是不同的。
0x08048457 <+58>: fstps 0x44(%esp)
0x0804848b <+110>: fistpl 0x3c(%esp)
添加gcc编译选项-mfpmath=387 -mno-sse
,可以重现(即使没有-m32,或者把float改成double)
像这样:
gcc -otest test.c -g -mfpmath=387 -mno-sse -m32
然后用gdb调试,断点在0x0804845b,并且运行 i=1
0x08048457 <+58>: fstps 0x44(%esp)
0x0804845b <+62>: flds 0x44(%esp)
(gdb) info float
=>R7: Valid 0x4003f7ffff8000000000 +30.99999904632568359
R6: Empty 0x4002a000000000000000
R5: Empty 0x00000000000000000000
R4: Empty 0x00000000000000000000
R3: Empty 0x00000000000000000000
R2: Empty 0x00000000000000000000
R1: Empty 0x00000000000000000000
R0: Empty 0x00000000000000000000
Status Word: 0x3820 PE
TOP: 7
Control Word: 0x037f IM DM ZM OM UM PM
PC: Extended Precision (64-bits)
RC: Round to nearest
Tag Word: 0x3fff
Instruction Pointer: 0x00:0x08048455
Operand Pointer: 0x00:0x00000000
Opcode: 0x0000
(gdb) x /xw 0x44+$esp
0xffffb594: 0x41f80000 ==> 31.0, s=0, M=1.1111 E=4
观察fstps的执行结果,
此时fpu上的控制寄存器上的RC值是Round to nearest.
fpu 寄存器上的值是 30.99999904632568359(80 位)。
0x44(%esp) (variable "f10"
) 上的值是 31.0。 (四舍五入)
然后用gdb调试,断点在0x0804848b,并且运行 i=1
0x0804848b <+110>: fistpl 0x3c(%esp)
(gdb) info float
=>R7: Valid 0x4003f7ffff8000000000 +30.99999904632568359
R6: Empty 0x4002a000000000000000
R5: Empty 0x00000000000000000000
R4: Empty 0x00000000000000000000
R3: Empty 0x00000000000000000000
R2: Empty 0x00000000000000000000
R1: Empty 0x00000000000000000000
R0: Empty 0x00000000000000000000
Status Word: 0x3820 PE
TOP: 7
Control Word: 0x0c7f IM DM ZM OM UM PM
PC: Single Precision (24-bits)
RC: Round toward zero
Tag Word: 0x3fff
Instruction Pointer: 0x00:0x08048485
Operand Pointer: 0x00:0x00000000
Opcode: 0x0000
此时fpu上控制寄存器的RC值向零舍入.
fpu 寄存器上的值是 30.99999904632568359(80 位)。值同上
很明显整数转换的时候,小数点被截掉了,值为30.
下面是main
反编译后的代码
(gdb) disas main
Dump of assembler code for function main:
0x0804841d <+0>: push %ebp
0x0804841e <+1>: mov %esp,%ebp
0x08048420 <+3>: and [=14=]xfffffff0,%esp
0x08048423 <+6>: sub [=14=]x50,%esp
0x08048426 <+9>: movl [=14=]x0,0x4c(%esp)
0x0804842e <+17>: jmp 0x80484de <main+193>
0x08048433 <+22>: fildl 0x4c(%esp)
0x08048437 <+26>: fldl 0x80485a8
0x0804843d <+32>: fmulp %st,%st(1)
0x0804843f <+34>: fldl 0x80485b0
0x08048445 <+40>: faddp %st,%st(1)
0x08048447 <+42>: fstps 0x48(%esp)
0x0804844b <+46>: flds 0x48(%esp)
0x0804844f <+50>: flds 0x80485b8
0x08048455 <+56>: fmulp %st,%st(1)
0x08048457 <+58>: fstps 0x44(%esp) // store to f10
0x0804845b <+62>: flds 0x44(%esp)
0x0804845f <+66>: fnstcw 0x2a(%esp)
0x08048463 <+70>: movzwl 0x2a(%esp),%eax
0x08048468 <+75>: mov [=14=]xc,%ah
0x0804846a <+77>: mov %ax,0x28(%esp)
0x0804846f <+82>: fldcw 0x28(%esp)
0x08048473 <+86>: fistpl 0x40(%esp)
0x08048477 <+90>: fldcw 0x2a(%esp)
0x0804847b <+94>: flds 0x48(%esp)
0x0804847f <+98>: fldl 0x80485c0
0x08048485 <+104>: fmulp %st,%st(1)
0x08048487 <+106>: fldcw 0x28(%esp)
0x0804848b <+110>: fistpl 0x3c(%esp) // f1 * 10 convert int
0x0804848f <+114>: fldcw 0x2a(%esp)
0x08048493 <+118>: flds 0x48(%esp)
0x08048497 <+122>: fldl 0x80485c0
0x0804849d <+128>: fmulp %st,%st(1)
0x0804849f <+130>: flds 0x44(%esp)
0x080484a3 <+134>: fxch %st(1)
0x080484a5 <+136>: mov 0x3c(%esp),%eax
0x080484a9 <+140>: mov 0x40(%esp),%edx
0x080484ad <+144>: sub %eax,%edx
0x080484af <+146>: mov %edx,%eax
0x080484b1 <+148>: fstpl 0x18(%esp)
0x080484b5 <+152>: fstpl 0x10(%esp)
0x080484b9 <+156>: mov %eax,0xc(%esp)
0x080484bd <+160>: mov 0x3c(%esp),%eax
0x080484c1 <+164>: mov %eax,0x8(%esp)
0x080484c5 <+168>: mov 0x40(%esp),%eax
0x080484c9 <+172>: mov %eax,0x4(%esp)
0x080484cd <+176>: movl [=14=]x8048588,(%esp)
0x080484d4 <+183>: call 0x80482f0 <printf@plt>
0x080484d9 <+188>: addl [=14=]x1,0x4c(%esp)
0x080484de <+193>: cmpl [=14=]x14,0x4c(%esp)
0x080484e3 <+198>: jle 0x8048433 <main+22>
0x080484e9 <+204>: leave
0x080484ea <+205>: ret
我目前正在使用需要 运行 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 位系统上使用 -m32
修饰符编译(使用 gcc)程序的输出是:
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
and/or int
更改为 short
或 long
,该问题也存在如果程序编译为 64 位,则不会显示。
澄清一下,我在这里试图了解的问题不是关于浮点 arithmetic/rounding,而是 32 位内存处理的差异。
该问题已在以下平台上进行测试:
Linux 版本 4.15.0-45-generic (buildd@lgw01-amd64-031) (gcc 版本 7.3.0 (Ubuntu 7.3.0-16ubuntu3) ), 程序编译使用: gcc -m32 Cast32int.c
Linux 版本 2.4.20-8 (bhcompile@porky.devel.redhat.com) (gcc 版本 3.2.2 20030222 (Red Hat Linux 3.2.2-5 )), 程序编译使用: gcc Cast32int.c
感谢任何帮助我理解这里发生的事情的指示。
使用 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
汇编器生成的值将被转换为 64 位的 FPU 堆栈压入,然后相乘。对于 c1
,结果随后被转换回浮点数并存储,然后再次检索并放置在 FPU 堆栈中(并再次转换为双精度数)以调用 __ftol2_sse
、运行 -time 函数将 double 转换为 int。
对于c2
,中间值不是 与float 之间的转换,并立即传递给__ftol2_sse
函数。对于此功能,另请参阅 Convert double to 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
C 标准对于如何执行浮点运算不是很严格。该标准允许实现以比涉及的类型更高的精度进行计算。
您的结果很可能来自 c1
计算为 "float-to-int" 而 c2
计算为 "double-to-int"(或更高的精度) ).
这是显示相同行为的另一个示例。
#define DD 0.11111111
int main()
{
int i = 27;
int c1,c2,c3;
float f1;
double d1;
printf("%.60f\n", DD);
f1 = i * DD;
d1 = i * DD;
c1 = (int)f1;
c2 = (int)(i * DD);
c3 = (int)d1;
printf("----------------------\n");
printf("f1: %.60f\n", f1);
printf("d1: %.60f\n", d1);
printf("m : %.60f\n", i * DD);
printf("%d, %d, %d\n",c1,c2,c3);
}
我的输出:
0.111111109999999999042863407794357044622302055358886718750000
----------------------
f1: 3.000000000000000000000000000000000000000000000000000000000000
d1: 2.999999970000000182324129127664491534233093261718750000000000
m : 2.999999970000000182324129127664491534233093261718750000000000
3, 2, 2
这里的技巧是 0.11111111
中的个数。准确的结果是“2.99999997”。当您更改个数时,准确结果仍为“2.99...997”形式(即,当 1 的数量增加时,9 的数量增加)。
在某个点(也就是一些点)你会到达一个点,将结果存储在一个浮点数中,将结果四舍五入为“3.0”,而双精度仍然能够保持“2.999999 .....” .然后转换为 int 将给出不同的结果。
进一步增加 1 的数量将导致 double 也将四舍五入为“3.0”,因此转换为 int 将产生相同的结果。
在“32位系统”中,差异是由于f1*10.0
使用全double
精度,而f10
只有float
精度因为那是它的类型。 f1*10.0
使用 double
精度,因为 10.0
是一个 double
常量。当 f1*10.0
赋值给 f10
时,值发生变化,因为它被隐式转换为精度较低的 float
。
如果您改用 float
常量 10.0f
,差异就会消失。
考虑第一种情况,当i
为1时,则:
- 在
f1 = 3+i*0.1
中,0.1
是一个double
常数,所以在double
中进行运算,结果为3.100000000000000088817841970012523233890533447265625。然后,将其分配给f1
,它被转换为float
,产生 3.099999904632568359375. - 在
f10 = f1*10.0;
中,10.0
是一个double
常数,所以在double
中再次进行运算,结果为30.99999904632568359375。对于赋值给f10
,这个被转换为float
,结果是31. - 后面打印
f10
和f1*10.0
时,我们看到上面给出的值,小数点后九位,f10
为“31.000000000”,“30.999999046” ”.
如果您打印 f1*10.0f
,使用 float
常量 10.0f
而不是 double
常量 10.0
,结果将是“31.000000000”而不是比“30.999999046”。
(以上使用IEEE-754基本32位和64位二进制浮点运算。)
特别注意:f1*10.0
和 f10
之间的区别出现在 f1*10.0
转换为 float
以分配给 f10
时。虽然 C 允许实现在评估表达式时使用额外的精度,但它要求实现在赋值和强制转换中放弃这种精度。因此,在符合标准的编译器中,对 f10
的赋值必须 使用 float
精度。这意味着,即使程序是为“64 位系统”编译的,差异 也应该 出现。否则,编译器不符合 C 标准。
此外,如果将float
更改为double
,则不会发生向float
的转换,并且不会更改值。在这种情况下,f1*10.0
和 f10
之间应该没有区别。
鉴于问题报告的差异在“64 位”编译中没有体现出来,而在 double
中体现出来,是否准确报告观察结果值得怀疑。为澄清这一点,应显示确切的代码,并应由第三方复制观察结果。
主要原因是后面两行the rounding-control (RC) field of the x87 FPU control register
值不一致。最终c1和c2的值是不同的。
0x08048457 <+58>: fstps 0x44(%esp)
0x0804848b <+110>: fistpl 0x3c(%esp)
添加gcc编译选项-mfpmath=387 -mno-sse
,可以重现(即使没有-m32,或者把float改成double)
像这样:
gcc -otest test.c -g -mfpmath=387 -mno-sse -m32
然后用gdb调试,断点在0x0804845b,并且运行 i=1
0x08048457 <+58>: fstps 0x44(%esp)
0x0804845b <+62>: flds 0x44(%esp)
(gdb) info float
=>R7: Valid 0x4003f7ffff8000000000 +30.99999904632568359
R6: Empty 0x4002a000000000000000
R5: Empty 0x00000000000000000000
R4: Empty 0x00000000000000000000
R3: Empty 0x00000000000000000000
R2: Empty 0x00000000000000000000
R1: Empty 0x00000000000000000000
R0: Empty 0x00000000000000000000
Status Word: 0x3820 PE
TOP: 7
Control Word: 0x037f IM DM ZM OM UM PM
PC: Extended Precision (64-bits)
RC: Round to nearest
Tag Word: 0x3fff
Instruction Pointer: 0x00:0x08048455
Operand Pointer: 0x00:0x00000000
Opcode: 0x0000
(gdb) x /xw 0x44+$esp
0xffffb594: 0x41f80000 ==> 31.0, s=0, M=1.1111 E=4
观察fstps的执行结果,
此时fpu上的控制寄存器上的RC值是Round to nearest.
fpu 寄存器上的值是 30.99999904632568359(80 位)。
0x44(%esp) (variable "f10"
) 上的值是 31.0。 (四舍五入)
然后用gdb调试,断点在0x0804848b,并且运行 i=1
0x0804848b <+110>: fistpl 0x3c(%esp)
(gdb) info float
=>R7: Valid 0x4003f7ffff8000000000 +30.99999904632568359
R6: Empty 0x4002a000000000000000
R5: Empty 0x00000000000000000000
R4: Empty 0x00000000000000000000
R3: Empty 0x00000000000000000000
R2: Empty 0x00000000000000000000
R1: Empty 0x00000000000000000000
R0: Empty 0x00000000000000000000
Status Word: 0x3820 PE
TOP: 7
Control Word: 0x0c7f IM DM ZM OM UM PM
PC: Single Precision (24-bits)
RC: Round toward zero
Tag Word: 0x3fff
Instruction Pointer: 0x00:0x08048485
Operand Pointer: 0x00:0x00000000
Opcode: 0x0000
此时fpu上控制寄存器的RC值向零舍入.
fpu 寄存器上的值是 30.99999904632568359(80 位)。值同上
很明显整数转换的时候,小数点被截掉了,值为30.
下面是main
反编译后的代码
(gdb) disas main
Dump of assembler code for function main:
0x0804841d <+0>: push %ebp
0x0804841e <+1>: mov %esp,%ebp
0x08048420 <+3>: and [=14=]xfffffff0,%esp
0x08048423 <+6>: sub [=14=]x50,%esp
0x08048426 <+9>: movl [=14=]x0,0x4c(%esp)
0x0804842e <+17>: jmp 0x80484de <main+193>
0x08048433 <+22>: fildl 0x4c(%esp)
0x08048437 <+26>: fldl 0x80485a8
0x0804843d <+32>: fmulp %st,%st(1)
0x0804843f <+34>: fldl 0x80485b0
0x08048445 <+40>: faddp %st,%st(1)
0x08048447 <+42>: fstps 0x48(%esp)
0x0804844b <+46>: flds 0x48(%esp)
0x0804844f <+50>: flds 0x80485b8
0x08048455 <+56>: fmulp %st,%st(1)
0x08048457 <+58>: fstps 0x44(%esp) // store to f10
0x0804845b <+62>: flds 0x44(%esp)
0x0804845f <+66>: fnstcw 0x2a(%esp)
0x08048463 <+70>: movzwl 0x2a(%esp),%eax
0x08048468 <+75>: mov [=14=]xc,%ah
0x0804846a <+77>: mov %ax,0x28(%esp)
0x0804846f <+82>: fldcw 0x28(%esp)
0x08048473 <+86>: fistpl 0x40(%esp)
0x08048477 <+90>: fldcw 0x2a(%esp)
0x0804847b <+94>: flds 0x48(%esp)
0x0804847f <+98>: fldl 0x80485c0
0x08048485 <+104>: fmulp %st,%st(1)
0x08048487 <+106>: fldcw 0x28(%esp)
0x0804848b <+110>: fistpl 0x3c(%esp) // f1 * 10 convert int
0x0804848f <+114>: fldcw 0x2a(%esp)
0x08048493 <+118>: flds 0x48(%esp)
0x08048497 <+122>: fldl 0x80485c0
0x0804849d <+128>: fmulp %st,%st(1)
0x0804849f <+130>: flds 0x44(%esp)
0x080484a3 <+134>: fxch %st(1)
0x080484a5 <+136>: mov 0x3c(%esp),%eax
0x080484a9 <+140>: mov 0x40(%esp),%edx
0x080484ad <+144>: sub %eax,%edx
0x080484af <+146>: mov %edx,%eax
0x080484b1 <+148>: fstpl 0x18(%esp)
0x080484b5 <+152>: fstpl 0x10(%esp)
0x080484b9 <+156>: mov %eax,0xc(%esp)
0x080484bd <+160>: mov 0x3c(%esp),%eax
0x080484c1 <+164>: mov %eax,0x8(%esp)
0x080484c5 <+168>: mov 0x40(%esp),%eax
0x080484c9 <+172>: mov %eax,0x4(%esp)
0x080484cd <+176>: movl [=14=]x8048588,(%esp)
0x080484d4 <+183>: call 0x80482f0 <printf@plt>
0x080484d9 <+188>: addl [=14=]x1,0x4c(%esp)
0x080484de <+193>: cmpl [=14=]x14,0x4c(%esp)
0x080484e3 <+198>: jle 0x8048433 <main+22>
0x080484e9 <+204>: leave
0x080484ea <+205>: ret