从 double 转换总是返回零
Posted
技术标签:
【中文标题】从 double 转换总是返回零【英文标题】:Casting from double always returns zero 【发布时间】:2020-03-31 13:27:14 【问题描述】:为什么下面的代码用clang编译时返回零?
#include <stdint.h>
uint64_t square()
return ((uint32_t)((double)4294967296.0)) - 10;
它产生的程序集是:
push rbp
mov rbp, rsp
xor eax, eax
pop rbp
ret
我本来希望 double 会变成零(整数),而减号会环绕它。换句话说,为什么要减去什么数字并不重要,因为它总是产生零?请注意,gcc 确实会产生不同的数字,正如预期的那样:
push rbp
mov rbp, rsp
mov eax, 4294967285
pop rbp
ret
我认为将 4294967296.0 转换为 uint32_t
是未定义的行为,但即便如此,我仍希望针对不同的减数产生不同的结果。
【问题讨论】:
is undefined behaviour but even then, I would expect
- 不。如果某些事情是未定义的行为,你不会想到。你不能指望。它几乎是按字面意思定义的——如果某事是未定义的行为,你不能指望代码做任何事情。
顺便说一句,(double)
“cast” 在这里什么都不做,因为字面量已经是 double
类型了。
由于未定义的行为是未定义的,如果系统每次遇到未定义的行为时表现不同,那就太好了。这就是DS9k 应该发生的事情——当然,除了你不能依赖于不同的行为:如果你试图利用它,DS9k 会破坏你的代码。
请指定您想要 C 答案还是 C++ 答案。尤其是在强制转换和未定义行为方面,这些语言差异很大,通常不应被视为相同。
【参考方案1】:
将超出范围的double
转换为unsigned
类型的行为确实未定义。
即使是unsigned
类型,也不适用环绕式。
这是一个经常被遗忘的规则。
一旦程序控制到达一个未定义的结构,整个程序就是未定义的,甚至有些自相矛盾的是,已经运行的语句。
参考:https://timsong-cpp.github.io/cppwp/conv.fpint#1
【讨论】:
可能要添加来源:timsong-cpp.github.io/cppwp/conv.fpint#1【参考方案2】:g++ 5.4.0 给出 4294967285(-11 无符号),因此它要由编译器来处理未定义的行为。
__Z6squarev:
LFB1023:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
movl $-11, %eax
movl $0, %edx
popl %ebp
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
【讨论】:
以上是关于从 double 转换总是返回零的主要内容,如果未能解决你的问题,请参考以下文章