Win32上将double类型转换为unsigned int会截断为2,147,483,648 [英] Double cast to unsigned int on Win32 is truncating to 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
在 Microsoft文档没有提及从 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:
整型值时的余数运算当将浮点型的值转换为无符号类型时,无需执行将其转换为无符号类型的操作。因此,可移植值的范围为 [0,Utype_MAX + 1)。
I认为问题可能是从2005年开始继续使用此功能-曾经有一个名为 __ 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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!