Double cast to unsigned int on Win32 is truncating to 2,147,483,648

前端 未结 3 1210
佛祖请我去吃肉
佛祖请我去吃肉 2020-12-30 18:22

Compiling the following code:

double getDouble()
{
    double value = 2147483649.0;
    return value;
}

int main()
{
           


        
3条回答
  •  粉色の甜心
    2020-12-30 18:52

    Following @AnttiHaapala's answer, I tested the code using optimization /Ox and found that this will remove the bug as __ftol2_sse is no longer used:

    //; 17   :     printf("Direct cast value: %u\n", (unsigned int)getDouble());
    
        push    -2147483647             //; 80000001H
        push    OFFSET $SG10116
        call    _printf
    
    //; 18   :     double d = getDouble();
    //; 19   :     printf("Indirect cast value: %u\n", (unsigned int)d);
    
        push    -2147483647             //; 80000001H
        push    OFFSET $SG10117
        call    _printf
        add esp, 28                 //; 0000001cH
    

    The optimizations inlined getdouble() and added constant expression evaluation thus removing the need for a conversion at runtime making the bug go away.

    Just out of curiosity, I made some more tests, namely changing the code to force float-to-int conversion at runtime. In this case the result is still correct, the compiler, with optimization, uses __dtoui3 in both conversions:

    //; 19   :     printf("Direct cast value: %u\n", (unsigned int)getDouble(d));
    
        movsd   xmm0, QWORD PTR _d$[esp+24]
        add esp, 12                 //; 0000000cH
        call    __dtoui3
        push    eax
        push    OFFSET $SG9261
        call    _printf
    
    //; 20   :     double db = getDouble(d);
    //; 21   :     printf("Indirect cast value: %u\n", (unsigned int)db);
    
        movsd   xmm0, QWORD PTR _d$[esp+20]
        add esp, 8
        call    __dtoui3
        push    eax
        push    OFFSET $SG9262
        call    _printf
    

    However, preventing inlining, __declspec(noinline) double getDouble(){...} will bring the bug back:

    //; 17   :     printf("Direct cast value: %u\n", (unsigned int)getDouble(d));
    
        movsd   xmm0, QWORD PTR _d$[esp+76]
        add esp, 4
        movsd   QWORD PTR [esp], xmm0
        call    _getDouble
        call    __ftol2_sse
        push    eax
        push    OFFSET $SG9261
        call    _printf
    
    //; 18   :     double db = getDouble(d);
    
        movsd   xmm0, QWORD PTR _d$[esp+80]
        add esp, 8
        movsd   QWORD PTR [esp], xmm0
        call    _getDouble
    
    //; 19   :     printf("Indirect cast value: %u\n", (unsigned int)db);
    
        call    __ftol2_sse
        push    eax
        push    OFFSET $SG9262
        call    _printf
    

    __ftol2_sse is called in both conversions making the output 2147483648 in both situations, @zwol suspicions were correct.


    Compilation details:

    • Using command line:
    cl /permissive- /GS /analyze- /W3 /Gm- /Ox /sdl /D "WIN32" program.c        
    
    • In Visual Studio:

      • Disabling RTC in Project -> Properties -> Code Generation and setting Basic Runtime Checks to default.

      • Enabling optimization in Project -> Properties -> Optimization and setting Optimization to /Ox.

      • With debugger in x86 mode.

提交回复
热议问题