了解双向int64_t转换

MK.*_*MK. 23 c++ double

所以我有两个函数,一个只是转换doubleint64_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;
}
Run Code Online (Sandbox Code Playgroud)

它们正常工作:cast(3.64)= 3round(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>
Run Code Online (Sandbox Code Playgroud)

我不知道是什么的这样做的目的callq上线14是对的,但,即便如此,my_cast而且my_round似乎只是做了cvttsd2si它,我认为这是转换与截断.

但是,这两个函数,如我之前提到的,在同一输入上产生不同的(正确的)值(比方说3.64)

怎么了?

Ant*_*vin 18

程序集输出更有用(g++ ... -S && cat round.s):

...
_Z7my_castd:
.LFB225:
    .cfi_startproc
    cvttsd2siq  %xmm0, %rax
    ret
    .cfi_endproc
...
_Z8my_roundd:
.LFB226:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    call    round             <<< This is what callq 19 means
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    cvttsd2siq  %xmm0, %rax
    ret
    .cfi_endproc
Run Code Online (Sandbox Code Playgroud)

如您所见,my_round调用std::round然后执行cvttsd2siq指令.这是因为std::round(double)返回double,因此其结果仍然必须转换为int64_t.这就是cvttsd2siq你们两个职能部门的工作.


man*_*lio 18

使用g ++,您可以更高级地查看使用该-fdump-tree-optimized开关发生的情况:

$ g++ -std=c++1y -c -O3 -fdump-tree-optimized ./round.cpp
Run Code Online (Sandbox Code Playgroud)

这会产生一个round.cpp.165t.optimized文件:

;; Function int64_t my_cast(double) (_Z7my_castd, funcdef_no=224, decl_uid=4743$

int64_t my_cast(double) (double d)
{
  long int t;

  <bb 2>:
  t_2 = (long int) d_1(D);
  return t_2;
}


;; Function int64_t my_round(double) (_Z8my_roundd, funcdef_no=225, decl_uid=47$

int64_t my_round(double) (double d)
{
  double t;
  int64_t _3;

  <bb 2>:
  t_2 = round (d_1(D));
  _3 = (int64_t) t_2;
  return _3;
}
Run Code Online (Sandbox Code Playgroud)

这里的差异非常明显(以及对round函数眩目的调用).


ach*_*ach 12

转储目标文件时objdump -d,添加选项非常重要,该选项-r命令实用程序也转储重定位:

$ objdump -dr round.o
...
0000000000000010 <_Z8my_roundd>:
  10:   48 83 ec 28             sub    $0x28,%rsp
  14:   e8 00 00 00 00          callq  19 <_Z8my_roundd+0x9>
                        15: R_X86_64_PC32       _ZSt5roundd
  19:   48 83 c4 28             add    $0x28,%rsp
  1d:   f2 48 0f 2c c0          cvttsd2si %xmm0,%rax
Run Code Online (Sandbox Code Playgroud)

现在,请注意出现的新行.这是一个体现在目标文件中的重定位指令.它指示链接器添加之间的距离_Z8my_roundd+0x9,并_ZSt5roundd在偏移15中找到的值.

e8偏移量14是用于相对于呼叫的操作代码.以下4个字节必须包含被调用函数的IP相对偏移量(执行时指向下一条指令的IP).因为编译器无法知道该距离,所以它会使其填充零并插入重定位,以便链接器可以在以后填充它.

在没有-r选项的情况下进行反汇编时,将忽略重定位,这会产生函数_Z8my_roundd调用到其中间的错觉.