本文介绍了Win32上将double类型转换为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):

Outputs (MSVC x86):

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

输出(MSVC x64):

Outputs (MSVC x64):

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

在没有提及从 double 到 unsigned int 。

INT_MAX 以上的所有值都将被截断为 2147483648 是函数的返回。

All values above INT_MAX are being truncated to 2147483648 when it is the return of a function.

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

I'm using Visual Studio 2019 to build the program. This doesn't happen on gcc.

我做错什么了吗?有没有安全的方法可以将 double 转换为 unsigned int ?

Am I doing someting wrong? Is there a safe way to convert double to unsigned int?

推荐答案

编译器错误...

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

From assembly provided by @anastaciu, the direct cast code calls __ftol2_sse, which seems to convert the number to a signed long. The routine name is ftol2_sse because this is an sse-enabled machine - but the float is in a x87 floating point register.

; 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

另一方面,间接转换确实可以实现

The indirect cast on the other hand does

; 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

弹出并存储将double值添加到本地变量,然后将其加载到SSE寄存器中,并调用 __ dtoui3 ,这是对double进行无符号int转换的例程...

which pops and stores the double value to the local variable, then loads it into a SSE register and calls __dtoui3 which is a double to unsigned int conversion routine...

直接投射的行为不符合C89;也不符合以后的任何修订版本- C89明确表示:

The behaviour of the direct cast does not conform to C89; nor does it conform to any later revision - even C89 explicitly says that:




I认为问题可能是-曾经有一个名为 __ ftol2 的转换函数可能会对此代码起作用,即它将值转换为带符号的数字 -2147483647,当解释一个无符号的数字时,它将产生正确的结果。


I believe the problem might be a continuation of this from 2005 - there used to be a conversion function called __ftol2 which probably would have worked for this code, i.e. it would have converted the value to a signed number -2147483647, which would have produced the correct result when interpreted an unsigned number.

不幸的是, __ ftol2_sse 并不能代替 __ ftol2 而是直接替换-最低有效值位-通过返回 LONG_MIN / 0x80000000 来表示超出范围的错误,在此处解释为无符号长,根本不是预期的结果。 __ ftol2_sse 的行为将对带符号的长有效,因为将双精度值转换为> LONG_MAX 到签名长的将具有不确定的行为。

Unfortunately __ftol2_sse is not a drop-in replacement for __ftol2, as it would - instead of just taking the least-significant value bits as-is - signal the out-of-range error by returning LONG_MIN / 0x80000000, which, interpreted as unsigned long here is not at all what was expected. The behaviour of __ftol2_sse would be valid for signed long, as conversion of a double a value > LONG_MAX to signed long would have undefined behaviour.

这篇关于Win32上将double类型转换为unsigned int会截断为2,147,483,648的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-20 16:10