将double转换为int?

时间:2013-09-03 08:27:11

标签: c type-conversion double

我的代码如下:

int main(int argc, char *argv[])
{

    double f = 18.40;
    printf("%d\n", (int)(10 * f));

    return 0;
}

VC6.0中的结果为184,而Codeblock中的结果为183.为什么?

4 个答案:

答案 0 :(得分:8)

原因是GCC试图尽可能地使代码向后兼容CPU的旧架构,而MSVC试图利用架构的新未来。

MSVC生成的代码将两个数字相乘,即10.0×18.40:

.text:00401006 fld     ds:dbl_40D168
.text:0040100C fstp    [ebp+var_8]
.text:0040100F fld     ds:dbl_40D160
.text:00401015 fmul    [ebp+var_8]
.text:00401018 call    __ftol2_sse

然后调用一个名为__ftol2_sse的函数,在这个函数中,它使用一些名为cvttsd2si的指令将结果转换为整数:

.text:00401189 push    ebp
.text:0040118A mov     ebp, esp
.text:0040118C sub     esp, 8
.text:0040118F and     esp, 0FFFFFFF8h
.text:00401192 fstp    [esp+0Ch+var_C]
.text:00401195 cvttsd2si eax, [esp+0Ch+var_C]
.text:0040119A leave
.text:0040119B retn

此说明cvttsd2si符合此page

  

转换标量双精度浮点值(带截断)   签名双字整数(SSE2)的双字

它基本上将double转换为整数。该指令是Intel Pentium 4引入的名为SSE2的指令集的一部分。

GCC默认不使用此指令集,并尝试使用i386提供的指令进行操作:

fldl   0x28(%esp)
fldl   0x403070
fmulp  %st,%st(1)
fnstcw 0x1e(%esp)
mov    0x1e(%esp),%ax
mov    $0xc,%ah
mov    %ax,0x1c(%esp)
fldcw  0x1c(%esp)
fistpl 0x18(%esp)
fldcw  0x1e(%esp)
mov    0x18(%esp),%eax
mov    %eax,0x4(%esp)
movl   $0x403068,(%esp)
call   0x401b44 <printf>
mov    $0x0,%eax

如果您希望GCC使用cvttsd2si,您需要通过使用标记-msse2来告诉它使用SSE2提供的期货,但这也意味着仍然使用旧计算机的某些人赢了我能够运行这个程序。有关更多选项,请参阅此处Intel 386 and AMD x86-64 Options

因此在使用-msse2进行编译后,它将使用cvttsd2si将结果转换为32位整数:

0x004013ac <+32>:    movsd  0x18(%esp),%xmm1
0x004013b2 <+38>:    movsd  0x403070,%xmm0
0x004013ba <+46>:    mulsd  %xmm1,%xmm0
0x004013be <+50>:    cvttsd2si %xmm0,%eax
0x004013c2 <+54>:    mov    %eax,0x4(%esp)
0x004013c6 <+58>:    movl   $0x403068,(%esp)
0x004013cd <+65>:    call   0x401b30 <printf>
0x004013d2 <+70>:    mov    $0x0,%eax

现在MSVC和GCC应该给出相同的数字:

> type test.c
#include <stdio.h>

int main(int argc, char *argv[])
{

    double f = 18.40;
    printf("%d\n", (int) (10.0  * f));

    return 0;
}
> gcc -Wall test.c -o gcctest.exe -msse2
> cl test.c /W3 /link /out:msvctest.exe
> gcctest.exe
184
> msvctest.exe
184
>

答案 1 :(得分:2)

Codeblocks编译器可能像18.39999999999那样作为浮点值。如果你想要一致的结果,我想你应该回合。

答案 2 :(得分:2)

关键是,0.42/5。在分母中具有除幂2之外的任何分数在浮点数中不能完全表示,很像1/3不能精确表示为十进制数。因此,您的编译器必须选择一个附近的可表示数字,其结果是10*18.4不是184,而是183.999 ......

现在,一切都取决于浮点数转换为整数时使用的舍入模式。使用舍入到最接近的舍入到无穷大,得到184舍入为零舍入为无穷大你得到183

答案 3 :(得分:1)

浮点计算由不同的编译器和不同的体系结构实现。即使是相同的编译器也可以使用不同的操作模式,从而产生不同的结果。

例如,如果我拿你的程序和我的gcc安装(MinGW,4.6.2)并编译如下:

gcc main.c

然后输出是,作为您的报告,183。

但是,如果我这样编译:

gcc main.c -ffloat-store

然后输出为184。

如果您真的想了解指定精确编译器版本所需的差异,并指定要传递给编译器的选项。

更重要的是,您应该知道值18.4不能完全表示为二进制浮点值。 closest representable double precision value to 18.4是:

18.39999 99999 99998 57891 45284 79799 62825 77514 64843 75

所以我怀疑你推断你的程序的正确输出是184。但我怀疑推理存在缺陷,无法解释可表示性,四舍五入等问题。