long float ejemplos ejemplo definicion data and c++ double

float - long c++



Entendiendo la conversión de doble a int64_t (3)

Al volcar un archivo de objeto con objdump -d , es muy importante agregar la opción -r , que ordena a la utilidad que también vuelque las reubicaciones:

$ 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

Ahora, note la nueva línea que apareció. Esa es una instrucción de reubicación incorporada en el archivo objeto. Le indica al enlazador que agregue una distancia entre _Z8my_roundd+0x9 y _ZSt5roundd al valor encontrado en el desplazamiento 15.

El e8 en el desplazamiento 14 es el código de operación para la llamada relativa. Los siguientes 4 bytes deben contener el desplazamiento relativo a la IP a la función que se está llamando (la IP en el momento de la ejecución que apunta a la siguiente instrucción). Debido a que el compilador no puede conocer esa distancia, la deja llena de ceros e inserta una reubicación para que el enlazador pueda llenarla más tarde.

Al desmontar sin la opción -r , las reubicaciones se ignoran, y eso crea la ilusión de que la función _Z8my_roundd realiza una llamada en el centro de sí misma.

Así que tengo dos funciones, una solo int64_t de double a int64_t , la otra llama 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; }

Funcionan correctamente: cast(3.64) = 3 y round(3.64) = 4 . Pero, cuando miro la asamblea, parecen estar haciendo lo mismo. Así que me pregunto cómo obtienen resultados diferentes?

$ 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>

No estoy seguro de cuál es el propósito de ese callq en la línea 14 , pero, aun así, my_cast y my_round parecen estar simplemente haciendo un cvttsd2si que, creo, es conversión con truncamiento.

Sin embargo, las dos funciones, como mencioné anteriormente, producen valores diferentes (correctos) en la misma entrada (por ejemplo, 3.64 )

¿Que esta pasando?


Con g ++ puede tener una vista de nivel superior de lo que está sucediendo con el conmutador -fdump-tree-optimized :

$ g++ -std=c++1y -c -O3 -fdump-tree-optimized ./round.cpp

Eso produce un archivo 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; }

Aquí las diferencias son bastante claras (y la llamada a la función de round es evidente).


El resultado del ensamblaje es más útil ( 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

Como puede ver, my_round llama a std::round y luego ejecuta la instrucción cvttsd2siq . Esto se debe a que std::round(double) devuelve el double , por lo que su resultado aún debe convertirse a int64_t . Y eso es lo que hace cvttsd2siq en tus dos funciones.