簡體   English   中英

將雙精度轉換為整數?

[英]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.42/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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM