Visual Studio调试与发布版本:比较int和float不匹配

Visual Studio debug vs. release build: comparing int and float missmatch

本文关键字:比较 int 不匹配 float 版本 调试 Studio 布版本 Visual      更新时间:2023-10-16

看看这个例子:

#include <stdio.h>
int main() {
  int i= 16777217;
  float f = 16777216.0;
  float g = i;
  if( i == f )
    printf("eqn");
  else
    printf("neqn");
  if( g == f )
    printf("eqn");
  else
    printf("neqn");
  return 0;
}

在发布模式下使用Visual Studio 2010 C++(VS),gcc或g++(4.9.2),具有输出

eq
eq

这对我来说是合理的:在第一次比较期间,i被隐式地转换为浮点,其中尾数中的有效位被截断。因此,if都具有与相等性相比相同的比特模式。在第二个if中,应用相同的转换,但是在定义和初始化g时已经执行了转换。

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

neq
eq

似乎没有应用第一个if中比较期间的隐式转换(作为C和C++中常见算术转换的一部分)。这是真的吗?有没有VS机制可以防止在比较浮点和int时出现这种误报(更精确地转换为int/foat)?根据MSDN VS C++遵循的标准。

我已经用这个函数检查了比特表示。对于所有编译器,它都会屈服于

i = 00000001000000000000000000000001
f = 01001011100000000000000000000000
g = 01001011100000000000000000000000

VS上的float.h状态为#define FLT_MANT_DIG 24,因此所描述的截断问题也应该成立。

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

EDIT:汇编程序代码附加

差异_debug.am

; Listing generated by Microsoft (R) Optimizing Compiler Version 16.00.30319.01 
    TITLE   c:Usersuserdocumentsvisual studio 2010Projectsdifferencesdifferencesdifferences.cpp
    .686P
    .XMM
    include listing.inc
    .model  flat
INCLUDELIB MSVCRTD
INCLUDELIB OLDNAMES
PUBLIC  ??_C@_04LMPLCMBC@neq?6?$AA@         ; `string'
PUBLIC  ??_C@_03HNJPMNDP@eq?6?$AA@          ; `string'
PUBLIC  __real@4b800000
PUBLIC  _wmain
EXTRN   __imp__printf:PROC
EXTRN   __fltused:DWORD
EXTRN   __RTC_CheckEsp:PROC
EXTRN   __RTC_Shutdown:PROC
EXTRN   __RTC_InitBase:PROC
;   COMDAT ??_C@_04LMPLCMBC@neq?6?$AA@
; File c:usersuserdocumentsvisual studio 2010projectsdifferencesdifferencesdifferences.cpp
CONST   SEGMENT
??_C@_04LMPLCMBC@neq?6?$AA@ DB 'neq', 0aH, 00H      ; `string'
CONST   ENDS
;   COMDAT ??_C@_03HNJPMNDP@eq?6?$AA@
CONST   SEGMENT
??_C@_03HNJPMNDP@eq?6?$AA@ DB 'eq', 0aH, 00H        ; `string'
CONST   ENDS
;   COMDAT __real@4b800000
CONST   SEGMENT
__real@4b800000 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 __real@4b800000
    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 $LN4@wmain
; Line 14
    mov esi, esp
    push    OFFSET ??_C@_03HNJPMNDP@eq?6?$AA@
    call    DWORD PTR __imp__printf
    add esp, 4
    cmp esi, esp
    call    __RTC_CheckEsp
; Line 15
    jmp SHORT $LN3@wmain
$LN4@wmain:
; Line 16
    mov esi, esp
    push    OFFSET ??_C@_04LMPLCMBC@neq?6?$AA@
    call    DWORD PTR __imp__printf
    add esp, 4
    cmp esi, esp
    call    __RTC_CheckEsp
$LN3@wmain:
; Line 19
    fld DWORD PTR _g$[ebp]
    fld DWORD PTR _f$[ebp]
    fucompp
    fnstsw  ax
    test    ah, 68                  ; 00000044H
    jp  SHORT $LN2@wmain
; Line 20
    mov esi, esp
    push    OFFSET ??_C@_03HNJPMNDP@eq?6?$AA@
    call    DWORD PTR __imp__printf
    add esp, 4
    cmp esi, esp
    call    __RTC_CheckEsp
; Line 21
    jmp SHORT $LN1@wmain
$LN2@wmain:
; Line 22
    mov esi, esp
    push    OFFSET ??_C@_04LMPLCMBC@neq?6?$AA@
    call    DWORD PTR __imp__printf
    add esp, 4
    cmp esi, esp
    call    __RTC_CheckEsp
$LN1@wmain:
; 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

差异_租赁.am

; Listing generated by Microsoft (R) Optimizing Compiler Version 16.00.30319.01 
    TITLE   c:Usersuserdocumentsvisual studio 2010Projectsdifferencesdifferencesdifferences.cpp
    .686P
    .XMM
    include listing.inc
    .model  flat
INCLUDELIB OLDNAMES
PUBLIC  ??_C@_03HNJPMNDP@eq?6?$AA@          ; `string'
PUBLIC  ??_C@_04LMPLCMBC@neq?6?$AA@         ; `string'
EXTRN   @__security_check_cookie@4:PROC
EXTRN   __imp__printf:PROC
;   COMDAT ??_C@_04LMPLCMBC@neq?6?$AA@
CONST   SEGMENT
??_C@_04LMPLCMBC@neq?6?$AA@ DB 'neq', 0aH, 00H      ; `string'
CONST   ENDS
;   COMDAT ??_C@_03HNJPMNDP@eq?6?$AA@
CONST   SEGMENT
??_C@_03HNJPMNDP@eq?6?$AA@ 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:usersuserdocumentsvisual studio 2010projectsdifferencesdifferencesdifferences.cpp
; Line 7
    push    esi
; Line 14
    mov esi, DWORD PTR __imp__printf
    push    OFFSET ??_C@_03HNJPMNDP@eq?6?$AA@
    call    esi
; Line 20
    push    OFFSET ??_C@_03HNJPMNDP@eq?6?$AA@
    call    esi
    add esp, 8
; Line 24
    xor eax, eax
    pop esi
; Line 26
    ret 0
_wmain  ENDP
_TEXT   ENDS
END

如果我们对版本ASM:进行解包

; Line 14
    push    OFFSET ??_C@_03HNJPMNDP@eq?6?$AA@
    call    DWORD PTR __imp__printf
    add esp, 4
; Line 18
    xor eax, eax
; Line 20
    ret 0

它只是打印eq并退出,这表明浮动比较刚刚完全优化。对于调试程序集,我们使用fldfild指令看到它:

; Line 9
    fld DWORD PTR __real@4b800000
    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的评论基本上证实了我刚才所说的。

两组输出都符合C行为。

在执行FP数学时,C允许FP计算以比操作数格式更高的精度级别进行。

如果代码执行i == f作为double数学运算,则结果为"neq"
如果代码将i == f执行为float数学,则结果为"eq"

int i= 16777217;
float f = 16777216.0;
if( i == f )
  printf("eqn");
else
  printf("neqn");

除了赋值和强制转换(去除所有额外的范围和精度)外,具有浮动操作数的运算符、经过常规算术转换的值和浮动常量产生的值将按照范围和精度可能大于C11§5.2.4.2.29 类型要求的格式进行评估

现代的C编译器提供了FLT_EVAL_METHOD,它指示了所使用的内容。


有没有VS机制可以防止在比较浮点和int时出现这种误报(更精确地转换为int/foat)?

为了强制进行float比较,代码可以使用

if((float) i == f )

为了强制进行double比较,代码可以使用

if((double) i == f )