[英]Convert double to int?
我的代碼如下:
int main(int argc, char *argv[])
{
double f = 18.40;
printf("%d\n", (int)(10 * f));
return 0;
}
VC6.0的結果是184,而Codeblock的結果是183,為什么?
這樣做的原因是 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
,是根據這個頁面:
將標量雙精度浮點值(帶截斷)轉換為四字整數的有符號雙字 (SSE2)
它基本上將雙精度轉換為整數。 該指令是 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 和 AMD x86-64 選項。
因此,與編譯后-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
>
Codeblocks 編譯器的浮點值可能類似於 18.39999999999。 如果你想要一個一致的結果,我認為你應該四舍五入。
關鍵是, 0.4
是2/5
。 分母中除 2 的冪以外的任何分數都不能完全用浮點數表示,就像 1/3 不能完全表示為十進制數一樣。 因此,您的編譯器必須選擇一個附近的、可表示的數字,結果10*18.4
不完全是184
,而是183.999
...
現在,一切都取決於浮點數轉換為整數時采用的舍入模式。 舍入到最近或舍入到無窮大,你得到184
,舍入到零或舍入到負無窮大你得到183
。
浮點計算由不同的編譯器和不同的體系結構實現。 即使是同一個編譯器也可以有不同的操作模式,從而產生不同的結果。
例如,如果我使用你的程序和我安裝的 gcc(MinGW,4.6.2)並像這樣編譯:
gcc main.c
那么輸出是,作為你的報告,183。
但是,如果我這樣編譯:
gcc main.c -ffloat-store
那么輸出是184。
如果您真的想了解差異,則需要指定精確的編譯器版本,並指定要傳遞給編譯器的選項。
更重要的是,您應該意識到值18.4
不能完全表示為二進制浮點值。 最接近18.4
可表示雙精度值是:
18.39999 99999 99998 57891 45284 79799 62825 77514 64843 75
所以我懷疑你在推理你的程序的正確輸出是184
。 但我懷疑推理是有缺陷的,無法解釋可表示性、四舍五入等問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.