首页 > 解决方案 > 在 Win32 上双重转换为 unsigned int 被截断为 2,147,483,648

问题描述

编译以下代码:

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

int main()
{
     printf("INT_MAX: %u\n", INT_MAX);
     printf("UINT_MAX: %u\n", UINT_MAX);

     printf("Double value: %f\n", getDouble());
     printf("Direct cast value: %u\n", (unsigned int) getDouble());
     double d = getDouble();
     printf("Indirect cast value: %u\n", (unsigned int) d);

     return 0;
}

输出(MSVC x86):

INT_MAX: 2147483647
UINT_MAX: 4294967295
Double value: 2147483649.000000
Direct cast value: 2147483648
Indirect cast value: 2147483649

输出(MSVC x64):

INT_MAX: 2147483647
UINT_MAX: 4294967295
Double value: 2147483649.000000
Direct cast value: 2147483649
Indirect cast value: 2147483649

Microsoft 文档double中,没有提到从到的转换中的有符号整数最大值unsigned int

当它是函数的返回时,上面的所有值INT_MAX都被截断。2147483648

我正在使用Visual Studio 2019来构建程序。这不会发生在gcc上。

我做错了吗?有没有安全的转换double方式unsigned int

标签: cvisual-c++castingx86floating-point

解决方案


一个编译器错误...

从@anastaciu 提供的程序集中,直接转换代码调用__ftol2_sse,这似乎将数字转换为有符号的 long。例程名称是ftol2_sse因为这是一台启用 sse 的机器 - 但浮点数位于 x87 浮点寄存器中。

; Line 17
    call    _getDouble
    call    __ftol2_sse
    push    eax
    push    OFFSET ??_C@_0BH@GDLBDFEH@Direct?5cast?5value?3?5?$CFu?6@
    call    _printf
    add esp, 8

另一方面,间接演员确实

; Line 18
    call    _getDouble
    fstp    QWORD PTR _d$[ebp]
; Line 19
    movsd   xmm0, QWORD PTR _d$[ebp]
    call    __dtoui3
    push    eax
    push    OFFSET ??_C@_0BJ@HCKMOBHF@Indirect?5cast?5value?3?5?$CFu?6@
    call    _printf
    add esp, 8

它将双精度值弹出并将其存储到局部变量中,然后将其加载到 SSE 寄存器__dtoui3中并调用双精度到无符号整数转换例程...

直接强制转换的行为不符合 C89;它也不符合任何以后的修订版——甚至C89 也明确表示:

当浮点类型的值转换为无符号类型时,不需要进行整数类型的值转换为无符号类型时进行的余数运算。因此可移植值的范围是[0, Utype_MAX + 1)


我相信这个问题可能是2005 年的延续- 曾经有一个调用的转换函数__ftol2可能适用于此代码,即它将值转换为有符号数-2147483647,这将产生正确的解释无符号数时的结果。

不幸__ftol2_sse的是,它不是 的直接替代品__ftol2,因为它会 - 而不是按原样采用最低有效值位 - 通过返回LONG_MIN/来表示超出范围的错误0x80000000,这里解释为 unsigned long 不是所有的预期。的行为对__ftol2_sse是有效的signed long,因为将双精度值 > 转换LONG_MAXsigned long将具有未定义的行为。


推荐阅读