Печать значения с плавающей запятой в STDOUT в сборке Linux - PullRequest
0 голосов
/ 28 мая 2019

Я пытаюсь вывести значения с плавающей точкой на консоль, но это дает неожиданные результаты, слишком большое число, имеющее другой знак.

Программа

.data
    float_: .asciz "%f\n"
    n1: .float 10.4
        n2: .float 10.3
.text

.globl main

main:
    sub $1, %esp
    finit
    flds n1
    fsubs n2
    fsts 1(%esp)

    movl 1(%esp), %eax
    call pfl
    addl $1, %esp

.exit:
    movl $1, %eax
    movl $0, %ebx
    int $0x80

pfl:
    pushal
    push %eax
    push $float_
    call printf
    add $8, %esp
    popal
    ret

выход

Каждый раз отличается, но от -400 ... 0,0 ... до -500000..0.0 ...

1 Ответ

3 голосов
/ 28 мая 2019

Различные проблемы с вашим кодом.Самое главное, %f ожидает double, но вы передаете float.См. man 3 printf или книгу C.Кроме того, float - это 4 байта, поэтому вам рекомендуется распределить 4 байта, а не 1. Чтобы еще хуже, вы даже не используете выделенный 1 байт, поскольку он равен (%esp), но вы использовали 1(%esp).Для дублирования вам понадобится 8 байтов.Далее вы забыли вытолкнуть значение из FPU.Кроме того, современные соглашения о вызовах требуют 16-байтового выровненного стека даже в 32-битном режиме.

Наконец, не рекомендуется использовать системный вызов exit непосредственно в main или другом коде, который использует функцию libc.Вместо этого просто ret или call exit, если вы действительно настаиваете на том, чтобы очистка libc (например, очистка буферов stdio) происходила.В противном случае вы не получите никакого вывода, если перенаправите стандартный вывод в файл, чтобы он был полностью буферизован.

Вот возможная версия, исправляющая все вышеперечисленное:

.data
    float_: .asciz "%f\n"
    n1: .float 10.4
    n2: .float 10.3
.text

.globl main

main:
    sub $12, %esp      # 8 bytes for double, + 4 bytes for alignment
    flds n1
    fsubs n2
    fstpl (%esp)       # pop double from fpu

    movl (%esp), %eax  # low 4 bytes
    movl 4(%esp), %edx # high 4 bytes
    call pfl
    addl $12, %esp

    ret

## input: EDX:EAX = the bit-pattern for a double
pfl:
    push %edx
    push %eax
    push $float_      # 3x push realigns the stack by 16 again
    call printf
    add $12, %esp
    ret

Отскок вашего double через целочисленные регистры нет необходимости;если вы ввели call printf в свою основную функцию, вы могли бы просто использовать fstpl, чтобы поместить double в стек прямо над указателем на строку формата.

Или ваша pfl функция принимает свои входные данные в %st(0) вместо целочисленных регистров, поскольку вы все равно составляете пользовательское соглашение о вызовах.

(Полагаю, ваш следующий вопросбудет почему он печатает 0.099999 вместо 0.1:))

...