9

Wystarczy popatrzeć na ten przykład:Visual Studio debugowania vs. kompilacji uwalniania: porównanie int i pływak Mów mi swatka

#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; 
} 

który za pomocą Visual Studio 2010 C++ (VS) w trybie zwolnienia, gcc lub g ++ (4,9 0,2), posiada wyjście

eq 
eq 

co jest rozsądne do mnie: Podczas pierwszego porównania i jest niejawnie konwertowane na float, gdzie znaczący bit mantysy jest skrócona. Tak więc, zarówno i, jak i f mają ten sam wzór bitowy, który porównuje się do równości. W drugim if obowiązuje ta sama konwersja, ale została już wykonana podczas definiowania i inicjowania g.

Jednak użycie VS w trybie debugowania, wynik jest

neq 
eq 

Wydaje niejawna konwersja podczas porównania w pierwszym if (w ramach zwykłych arytmetycznych konwersji w C i C++) nie jest stosowana . Czy to prawda? Czy istnieje mechanizm VS, który zapobiega takim fałszywym pozytywom w porównaniu floats i ints (konwersja do int/float z większą precyzją)? Według MSDN VS C++ podąża za standardem.

Sprawdziłem reprezentację bitową z this function. Dla wszystkich kompilatorów to daje do

i = 00000001000000000000000000000001 
f = 01001011100000000000000000000000 
g = 01001011100000000000000000000000 

float.h na VS stwierdza #define FLT_MANT_DIG 24 tak opisany problem obcięcia powinien posiadać również.

Skompilowałem wszystko na tym samym komputerze (Intel i5-3570K), ale dla VS w wirtualnym pudełku. Kompilacja z VS na innym komputerze również drukuje neq/eq.

EDIT: kod asemblera dołączony

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

Czy obejrzysz wygenerowanego kodu asemblera? Może powinieneś opublikować to tutaj. –

+0

FYI skompilowany z VS2015 wyświetla zawsze "eq eq" w trybie debugowania i zwolnienia. –

+0

Narzędzie do debugowania VS może używać plików FILD lub FICOMP, co pozwala uniknąć konwersji do zmiennej float (działa bezpośrednio z rejestrami float 80b). Opcja/fp może również wpływać na zachowanie. – Suma

Odpowiedz

6

Gdybyśmy demangle ASM wydaniu:

; 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 

Po prostu drukuje się eq i wychodzi, co sugeruje, że porównanie zmiennoprzecinkowe jest całkowicie zoptymalizowane. Do montażu debugowania, widzimy go przy użyciu fld i fild instrukcjami:

; 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 instrukcje który jest domyślnym architektura stosowane w Visual Studio 2010. Podejrzewam, że używasz /arch:SSE2 zamiast dostaniesz różne wyniki.

Hans Passant's comment zasadniczo potwierdza to, co właśnie powiedziałem.

+2

Tak, dodanie/arch: SSE2 do opcji kompilacji wyświetla 'eq/eq' również w trybie debugowania. Dzięki za wyjaśnienie dla użytkownika6320439 i HansPassant – johannes

0

Oba zestawy danych wyjściowych są zgodne z zachowaniem C.

Wykonując matematykę FP, C pozwala na wykonywanie obliczeń FP z wyższą precyzją niż format operandów.

Jeśli kod wykonuje i == f jako, wynik to "neq".
Jeśli kod wykonuje i == f jako float, wynik to "eq".

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

wyjątkiem przypisania i obsady (która usunąć wszystkie dodatkowe zasięg i precyzję), wartości uzyskane przez operatorów z pływającymi argumentów i wartości podlega zwykłym arytmetycznych konwersji i pływających stałych są oceniane na format, którego zakres i dokładność może być większa niż wymaga tego typu C11 §5.2.4.2.2 9

Współczesne kompilatory C świadczenia FLT_EVAL_METHOD który wskazuje, co jest wykorzystywane.


Czy VS-mechanizm, który zapobiega takich fałszywych alarmów w porównaniu pływaki oraz wskazówki (konwersja na int/pływaka z większą precyzją)?

wymusić float porównać, kod może użyć

if((float) i == f) 

wymusić double porównać, kod może użyć

if((double) i == f)