9

Date un'occhiata a questo esempio:Visual Studio di debug vs. build di rilascio: il confronto int e 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; 
} 

che, utilizzando Visual Studio 2010 C++ (VS) in modalità di rilascio, gcc o g ++ (4.9 .2), ha l'uscita

eq 
eq 

che è ragionevole per me: Durante il primo confronto, i viene implicitamente convertito ad un galleggiante dove viene troncato il bit significativo nella mantissa. Pertanto, sia i sia f hanno lo stesso schema di bit che si confronta con l'uguaglianza. Nel secondo if si applica la stessa conversione, ma è già stata eseguita durante la definizione e l'inizializzazione di g.

Tuttavia, utilizzando VS in modalità debug, il risultato è

neq 
eq 

Sembra, la conversione implicita durante confronto nella prima if (come parte delle usuali conversioni aritmetiche in C e C++) non viene applicato . È vero? Esiste un meccanismo VS che impedisce tali falsi positivi nel confronto tra float e int (conversione in int/float con maggiore precisione)? Secondo MSDN VS C++ segue lo standard.

Ho controllato la rappresentazione di bit con this function. Per tutti i compilatori cede al

i = 00000001000000000000000000000001 
f = 01001011100000000000000000000000 
g = 01001011100000000000000000000000 

float.h su VS afferma #define FLT_MANT_DIG 24 in modo che il problema descritto troncamento dovrebbe valere pure.

Ho compilato tutti sulla stessa macchina (Intel i5-3570K) ma per VS in una scatola virtuale. La compilazione con VS su un'altra macchina stampa anche neq/eq.

EDIT: codice assembler attaccato

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]?6?$[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

Hai guardato il codice assembler generato? Forse dovresti postarlo qui. –

+0

FYI compilato con VS2015 visualizza sempre 'eq eq' in modalità debug e release. –

+0

Il debug VS potrebbe utilizzare FILD o FICOMP, che evita la conversione float int (funziona direttamente con i registri float 80b). L'opzione/fp potrebbe anche influenzare il comportamento. – Suma

risposta

6

Se demangle l'ASM di rilascio:

; 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 

Si sta semplicemente stampando eq e si esce, il che suggerisce che il confronto fluttuante è completamente ottimizzato. Per il montaggio di debug, lo vediamo con fld e fild istruzioni:

; 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] 

Questi sono IA32 istruzioni che è l'architettura di default utilizzato in Visual Studio 2010. Ho il sospetto che si utilizza /arch:SSE2 invece si ottengono risultati diversi.

Hans Passant's comment sostanzialmente conferma quello che ho appena detto.

+2

Sì, aggiungendo/arch: SSE2 alle opzioni di compilazione stampa 'eq/eq' anche in modalità di debug. Grazie per la spiegazione a user6320439 e HansPassant – johannes

0

Entrambi i set di output sono conformi al comportamento C.

Nell'esecuzione della matematica FP, C consente di eseguire calcoli FP con un livello di precisione superiore rispetto al formato degli operandi.

Se il codice esegue i == f come double matematica, il risultato è "neq".
Se il codice esegue i == f come float matematica, il risultato è "eq".

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

Fatta eccezione per l'assegnazione e cast (che rimuovere tutta la gamma supplementare e precisione), i valori ottenuti da operatori con operandi galleggianti e valori soggetto alle usuali conversioni aritmetiche e costanti galleggianti sono valutati per un formato di cui la gamma e la precisione può essere maggiore di quanto richiesto dal tipo C11 §5.2.4.2.2 9

moderni compilatori C forniscono FLT_EVAL_METHOD che indica quello che viene utilizzato.


Esiste un VS-meccanismo che impedisce tali falsi positivi in ​​confronto galleggianti e int (conversione in int/float con più precisione)?

per forzare un float confronto, il codice potrebbe utilizzare

if((float) i == f) 

per forzare un double confronto, il codice potrebbe utilizzare

if((double) i == f) 
Problemi correlati