问题描述
编译以下代码:
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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!