2016-05-06 3 views
5

Я пытаюсь напечатать номер с плавающей запятой в сборке x86_64, но он просто печатает значение как ноль.Как напечатать одноточечный поплавок с printf

Есть еще несколько вопросов об этом. Один из них, по-видимому, был разрешен путем обеспечения того, чтобы you set the number of vector registers you're using in %al. Другой показал, что you need to have a stack alignment of 16 bytes. Но я делаю обе эти вещи и все еще не получаю правильный результат.

Это моя программа:

# 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 

ответ

5

printf(3)'s %f format specifier wants a double. Невозможно получить printf, чтобы принять float, только double или long double.

акции аргумента по умолчанию C в указать, что звонки на VARIADIC функции, такие как foo(char *fmt, ...) способствуют float к double и выполнять обычные целые акции целочисленных узких типов в int, для задней арг, которые соответствуют ... части прототипа. (То же самое относится ко всем аргументам для вызова функций без прототипа.) N1570 6.5.2.2 Function calls, subsections 6 and 7.

Таким образом, С не обеспечивает путь для вызывающего абонента, чтобы пропускать float к printf, так что это не имеет никакого преобразования для него, и %f означает double. (%lf обычно также работает для double, предполагая, что реализация игнорирует его для нецелых/wchar_t конверсий). Обратите внимание, что scanf отличается, потому что float * и double * не зависят от этих рекламных акций.


В этом случае нагрузки с CVTSS2SD .num, %xmm0.

Если вы посмотрите на compiler output, вы увидите НКУ делать все, что вы сделали, и pxor -нуль регистр первой разорвать ложную зависимость от старого значения %xmm0. (плохой дизайн cvtss2sd оставляет верхние 64 бита адресата без изменений.) gcc errs на стороне предостережения и вставляет инструкции xor-zeroing, чтобы во многих случаях прерывать ложные зависимости.


Вы, вероятно, получить 0 поскольку верхние биты XMM0 случиться равным нулю. Таким образом, битовая диаграмма для 123.4f в младших 32 битах мантиссы, когда printf смотрит на низкие 64 бит xmm0 как double (IEEE binary64 на x86).

Если вы попробуйте эквивалент с помощью float (например, на http://www.h-schmidt.net/FloatConverter/IEEE754.html), установив несколько бит в нижней половине, вы получите очень маленькое денормальное число. Если вы использовали %g (научная нотация) или %a (hex), появятся ненулевые биты. (Если в MXCSR не включен режим Denormals Areero).

+0

Удивительно для меня, что, несмотря на то, что я писал C++ много лет и многократно использовал printf ... Я никогда не знал этого. – cgmb

+1

@cgmb: Да, это неочевидно. Я забыл, но даже не возможно передать 'float' функции var-args в C. Правила продвижения указывают на преобразование в' double'. Я ожидаю, что если это будет возможно, по крайней мере, у GNU C будет спецификатор формата. (Как '% hf' или что-то еще).Для меня я действительно только привязался к этому ограничению от просмотра asm, так как в противном случае компилятор всегда выполняет преобразование для вас. (Кроме того, из-за необходимости печатать 'float' с форматированием в формате'% a' в шестнадцатеричном стиле) –

Смежные вопросы