【发布时间】:2016-03-23 01:17:31
【问题描述】:
所以我有两个函数,一个只是从double 转换为int64_t,另一个调用std::round:
std::int64_t my_cast(double d)
{
auto t = static_cast<std::int64_t>(d);
return t;
}
std::int64_t my_round(double d)
{
auto t = std::round(d);
return t;
}
它们工作正常:cast(3.64) = 3 和 round(3.64) = 4。但是,当我查看程序集时,他们似乎在做同样的事情。所以想知道他们如何得到不同的结果?
$ g++ -std=c++1y -c -O3 ./round.cpp -o ./round.o
$ objdump -dS ./round.o
./round.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <_Z7my_castd>:
0: f2 48 0f 2c c0 cvttsd2si %xmm0,%rax
5: c3 retq
6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
d: 00 00 00
0000000000000010 <_Z8my_roundd>:
10: 48 83 ec 08 sub $0x8,%rsp
14: e8 00 00 00 00 callq 19 <_Z7my_castd+0x19> <========!!!
19: 48 83 c4 08 add $0x8,%rsp
1d: f2 48 0f 2c c0 cvttsd2si %xmm0,%rax
22: c3 retq
Disassembly of section .text.startup:
0000000000000030 <_GLOBAL__sub_I__Z7my_castd>:
30: 48 83 ec 08 sub $0x8,%rsp
34: bf 00 00 00 00 mov $0x0,%edi
39: e8 00 00 00 00 callq 3e <_GLOBAL__sub_I__Z7my_castd+0xe>
3e: ba 00 00 00 00 mov $0x0,%edx
43: be 00 00 00 00 mov $0x0,%esi
48: bf 00 00 00 00 mov $0x0,%edi
4d: 48 83 c4 08 add $0x8,%rsp
51: e9 00 00 00 00 jmpq 56 <_Z8my_roundd+0x46>
我不确定14 线上的callq 的目的是什么,但即便如此,my_cast 和my_round 似乎只是在做一个cvttsd2si,我相信是截断转换。
但是,正如我之前提到的,这两个函数在相同的输入(比如3.64)上产生不同的(正确的)值
发生了什么?
【问题讨论】:
-
请注意,round(x) = trunc(x + 0.5)。我怀疑你没有正确识别这里的所有机器代码。
-
GCC 5.3.0 会调用
roundgcc.godbolt.org @Cheersandhth.-Alf 这仅适用于非负值 -
callq 19这是一个尚未解决的对std::round的引用。对象链接时填写。 -
@Cheers 和 hth。 - Alf 那是不正确的 - 这些操作是不等价的。例如,使用您的 cast 方法尝试
0.499999975 + 0.5或-1.4f + 0.5。然后尝试将0.499999975和-1.4传递给舍入函数。 -
@JesperJuhl:是的,我只是为 OP 指明了一个好的方向,我并没有提出实现的全部细节(例如,添加一个符号检查)。后者需要付出更多的努力。 ;-) 不过还是谢谢。