9

看一看这个例子:Visual Studio中调试与发布版本:比较int和float missmatch

#include <stdio.h> 

int main() { 

    int i= 16777217; 
    float f = 16777216.0; 
    float g = i; 

    if(i == f) 
    printf("eq\n"); 
    else 
    printf("neq\n"); 

    if(g == f) 
    printf("eq\n"); 
    else 
    printf("neq\n"); 

    return 0; 
} 

其中,使用Visual Studio 2010 C++(VS)在释放模式,GCC,或g ++(4.9 0.2),具有输出

eq 
eq 

这是合理的,我:在第一比较,i隐式转换为其中在尾数的显著位被截断的浮子。因此,if都具有相同的比较模式。在第二个if中,应用了相同的转换,但在定义和初始化g时已执行。

然而,在调试模式下使用VS,其结果是

neq 
eq 

看来,在第一if比较(如在C和C通常的算术转换++的一部分)中的隐式转换不被施加。这是真的?有没有一种VS机制可以在比较浮点数和整数(更精确地转换为int/float)时防止这种误报?根据MSDN VS C++遵循标准。

我已经检查了位表示与this function。对于所有的编译器它产生对VS

i = 00000001000000000000000000000001 
f = 01001011100000000000000000000000 
g = 01001011100000000000000000000000 

float.h指出#define FLT_MANT_DIG 24这样描述截断问题应持有为好。

我编译所有在同一台机器上(英特尔i5-3570K),但VS在一个虚拟的盒子。在另一台机器上编译VS也会打印neq/eq

编辑:汇编代码附加

differences_debug.asm

; Listing generated by Microsoft (R) Optimizing Compiler Version 16.00.30319.01 

    TITLE c:\Users\user\documents\visual studio 2010\Projects\differences\differences\differences.cpp 
    .686P 
    .XMM 
    include listing.inc 
    .model flat 

INCLUDELIB MSVCRTD 
INCLUDELIB OLDNAMES 

PUBLIC [email protected][email protected][email protected]   ; `string' 
PUBLIC [email protected][email protected][email protected]   ; `string' 
PUBLIC [email protected] 
PUBLIC _wmain 
EXTRN __imp__printf:PROC 
EXTRN __fltused:DWORD 
EXTRN __RTC_CheckEsp:PROC 
EXTRN __RTC_Shutdown:PROC 
EXTRN __RTC_InitBase:PROC 
; COMDAT [email protected][email protected][email protected] 
; File c:\users\user\documents\visual studio 2010\projects\differences\differences\differences.cpp 
CONST SEGMENT 
[email protected][email protected][email protected] DB 'neq', 0aH, 00H  ; `string' 
CONST ENDS 
; COMDAT [email protected][email protected][email protected] 
CONST SEGMENT 
[email protected][email protected][email protected] DB 'eq', 0aH, 00H  ; `string' 
CONST ENDS 
; COMDAT [email protected] 
CONST SEGMENT 
[email protected] DD 04b800000r   ; 1.67772e+007 
CONST ENDS 
; COMDAT rtc$TMZ 
rtc$TMZ SEGMENT 
__RTC_Shutdown.rtc$TMZ DD FLAT:__RTC_Shutdown 
rtc$TMZ ENDS 
; COMDAT rtc$IMZ 
rtc$IMZ SEGMENT 
__RTC_InitBase.rtc$IMZ DD FLAT:__RTC_InitBase 
; Function compile flags: /Odtp /RTCsu /ZI 
rtc$IMZ ENDS 
; COMDAT _wmain 
_TEXT SEGMENT 
_g$ = -32      ; size = 4 
_f$ = -20      ; size = 4 
_i$ = -8      ; size = 4 
_argc$ = 8      ; size = 4 
_argv$ = 12      ; size = 4 
_wmain PROC      ; COMDAT 
; Line 7 
    push ebp 
    mov ebp, esp 
    sub esp, 228    ; 000000e4H 
    push ebx 
    push esi 
    push edi 
    lea edi, DWORD PTR [ebp-228] 
    mov ecx, 57     ; 00000039H 
    mov eax, -858993460    ; ccccccccH 
    rep stosd 
; Line 8 
    mov DWORD PTR _i$[ebp], 16777217  ; 01000001H 
; Line 9 
    fld DWORD PTR [email protected] 
    fstp DWORD PTR _f$[ebp] 
; Line 10 
    fild DWORD PTR _i$[ebp] 
    fstp DWORD PTR _g$[ebp] 
; Line 13 
    fild DWORD PTR _i$[ebp] 
    fld DWORD PTR _f$[ebp] 
    fucompp 
    fnstsw ax 
    test ah, 68     ; 00000044H 
    jp SHORT [email protected] 
; Line 14 
    mov esi, esp 
    push OFFSET [email protected][email protected][email protected] 
    call DWORD PTR __imp__printf 
    add esp, 4 
    cmp esi, esp 
    call __RTC_CheckEsp 
; Line 15 
    jmp SHORT [email protected] 
[email protected]: 
; Line 16 
    mov esi, esp 
    push OFFSET [email protected][email protected][email protected] 
    call DWORD PTR __imp__printf 
    add esp, 4 
    cmp esi, esp 
    call __RTC_CheckEsp 
[email protected]: 
; Line 19 
    fld DWORD PTR _g$[ebp] 
    fld DWORD PTR _f$[ebp] 
    fucompp 
    fnstsw ax 
    test ah, 68     ; 00000044H 
    jp SHORT [email protected] 
; Line 20 
    mov esi, esp 
    push OFFSET [email protected][email protected][email protected] 
    call DWORD PTR __imp__printf 
    add esp, 4 
    cmp esi, esp 
    call __RTC_CheckEsp 
; Line 21 
    jmp SHORT [email protected] 
[email protected]: 
; Line 22 
    mov esi, esp 
    push OFFSET [email protected][email protected][email protected] 
    call DWORD PTR __imp__printf 
    add esp, 4 
    cmp esi, esp 
    call __RTC_CheckEsp 
[email protected]: 
; Line 24 
    xor eax, eax 
; Line 26 
    pop edi 
    pop esi 
    pop ebx 
    add esp, 228    ; 000000e4H 
    cmp ebp, esp 
    call __RTC_CheckEsp 
    mov esp, ebp 
    pop ebp 
    ret 0 
_wmain ENDP 
_TEXT ENDS 
END 

differences_release.asm

; Listing generated by Microsoft (R) Optimizing Compiler Version 16.00.30319.01 

    TITLE c:\Users\user\documents\visual studio 2010\Projects\differences\differences\differences.cpp 
    .686P 
    .XMM 
    include listing.inc 
    .model flat 

INCLUDELIB OLDNAMES 

PUBLIC [email protected][email protected][email protected]   ; `string' 
PUBLIC [email protected][email protected][email protected]   ; `string' 
EXTRN @[email protected]:PROC 
EXTRN __imp__printf:PROC 
; COMDAT [email protected][email protected][email protected] 
CONST SEGMENT 
[email protected][email protected][email protected] DB 'neq', 0aH, 00H  ; `string' 
CONST ENDS 
; COMDAT [email protected][email protected][email protected] 
CONST SEGMENT 
[email protected][email protected][email protected] DB 'eq', 0aH, 00H  ; `string' 
CONST ENDS 
PUBLIC _wmain 
EXTRN __fltused:DWORD 
; Function compile flags: /Ogtp 
; COMDAT _wmain 
_TEXT SEGMENT 
_argc$ = 8      ; size = 4 
_argv$ = 12      ; size = 4 
_wmain PROC      ; COMDAT 
; File c:\users\user\documents\visual studio 2010\projects\differences\differences\differences.cpp 
; Line 7 
    push esi 
; Line 14 
    mov esi, DWORD PTR __imp__printf 
    push OFFSET [email protected][email protected][email protected] 
    call esi 
; Line 20 
    push OFFSET [email protected][email protected][email protected] 
    call esi 
    add esp, 8 
; Line 24 
    xor eax, eax 
    pop esi 
; Line 26 
    ret 0 
_wmain ENDP 
_TEXT ENDS 
END 
+3

你查看生成的汇编代码?也许你应该在这里发布。 –

+0

用VS2015编译的FYI在调试和释放模式下总是显示'eq eq'。 –

+0

VS调试可能使用FILD或FICOMP,它可以避免int浮点转换(直接与80b浮点寄存器一起工作)。/fp选项也可能会影响行为。 – Suma

回答

6

如果我们还原函数释放ASM:

; Line 14 
    push OFFSET [email protected][email protected][email protected] 
    call DWORD PTR __imp__printf 
    add esp, 4 
; Line 18 
    xor eax, eax 
; Line 20 
    ret 0 

它只是打印eq并退出,这表明浮点比较只是完全优化了。对于调试组件,我们看到使用fldfild说明它:

; Line 9 
    fld DWORD PTR [email protected] 
    fstp DWORD PTR _f$[ebp] 
; Line 10 
    fild DWORD PTR _i$[ebp] 
    fstp DWORD PTR _g$[ebp] 
; Line 13 
    fild DWORD PTR _i$[ebp] 

这些IA32指令是在Visual Studio 2010中使用的默认架构我怀疑你使用/arch:SSE2,而不是你会得到不同的结果。

Hans Passant's comment从本质上证实了我刚才所说的。

+2

是,添加/拱。感谢user6320439和HansPassant的解释 – johannes

0

这两组输出都是符合C标准的行为。

在执行FP数学,C允许FP计算,以在更高的精度级比所述操作数的格式发生。

如果代码执行i == fdouble数学计算,则结果为"neq"
如果代码执行i == ffloat数学,结果是"eq"

int i= 16777217; 
float f = 16777216.0; 
if(i == f) 
    printf("eq\n"); 
else 
    printf("neq\n"); 

除了分配和流延(其去除所有多余的范围和精度),是由操作者的浮动操作数得到的值和值受到通常的算术转换和浮动常数进行评估,以比要求的类型C11§5.2.4.2.29

现代C编译器提供FLT_EVAL_METHOD其指示使用什么格式,其范围和精度可能会更大。


是否有一个VS-机构,其防止了这种误报在比较浮标和整数(转换为int /浮动更加精确)?

要强制float比较,代码可以使用

if((float) i == f) 

要强制double比较,代码可以使用

if((double) i == f) 
相关问题