Cómo imprimir un flotador de precisión simple con printf

Estoy tratando de imprimir un número de coma flotante en el ensamblaje x86_64, pero solo imprime el valor como cero.

Hay algunas preguntas sobre esto ya. Uno pareció resolverse asegurándose de establecer la cantidad de registros vectoriales que está usando en% al . Otro mostró que necesita tener una alineación de stack de 16 bytes . Pero, estoy haciendo ambas cosas y todavía no obtengo la salida correcta.

Este es mi progtwig:

# prints a floating point value .section .rodata .fmt: .string "num: %f\n" .num: .float 123.4 .section .text .global main .type main, @function main: subq $8, %rsp # 16-byte alignment # print my number movss .num, %xmm0 # load float value movq $.fmt, %rdi # load format string movb $1, %al # use 1 vector register call printf # exit addq $8, %rsp # undo alignment movq $0, %rax # return 0 ret 

printf(3) especificador de formato %f printf(3) quiere un double . No hay forma de que printf acepte un float , solo double o long double .

Las promociones de argumento predeterminadas de C especifican que las llamadas a funciones variadas como foo(char *fmt, ...) promueven float a double y realizan las promociones enteras usuales de tipos enteros estrechos a int , para argumentos finales que coinciden con la ... parte de El Prototipo. (Lo mismo se aplica a todos los argumentos para llamar a funciones sin prototipo). N1570 6.5.2.2 Llamadas de función, subsecciones 6 y 7 .

Por lo tanto, C no proporciona ninguna forma para que un llamante pase un float a printf , por lo que no tiene conversión para él, y %f significa double . ( %lf también funciona para el double , suponiendo que la implementación lo ignora para las conversiones no enteras / wchar_t . Se requiere aceptar %lf para el double en las implementaciones printf C99 / C11 y C ++ 11 , para que pueda usar el mismo formato %lf cadena con un double para printf y scanf ).

Tenga en cuenta que scanf es diferente porque float * y double * no se ven afectados por esas promociones.


En este caso, cargue con CVTSS2SD .num, %xmm0 .

Si observa la salida del comstackdor , verá que gcc hará todo lo que hizo y pxor -cero el registro primero para romper la dependencia falsa del antiguo valor de %xmm0 . (el diseño pobre de cvtss2sd deja sin cambios los 64 bits superiores del destino.) gcc erres en el lado de la precaución, e inserta instrucciones xor-zeroing para romper las dependencias falsas en muchos casos.


Probablemente obtengas 0 porque los bits superiores de xmm0 son cero . Cuando printf mira los 64 bits bajos de xmm0 como un double (IEEE binary64 en x86) , encuentra el patrón de bits para 123.4f en los 32 bits bajos de la mantisa, y el rest cero. Esto representa un número muy pequeño (denormal), por lo que sale como cero con %f .

Puede probar el equivalente con un float (por ejemplo, en http://www.h-schmidt.net/FloatConverter/IEEE754.html ), estableciendo algunos bits en la mitad inferior para ver lo que obtiene.

Si utilizó %g (notación científica) o %a (representación hexadecimal del patrón de bit double ), aparecerían los bits distintos de cero. (A menos que haya habilitado el modo Denormals Are Zero en el MXCSR).