Пример инструкции FXTRACT

Я написал этот код в NASM:

section .data
    fvar: dd 123.456
    fsig: dq 0.0
    fexp: dq 0.0

section .text
    fld dword[fVar]
    fxtract          ; put significand in ST(0), and exponent in ST(1)
    fstp qword[fsig] ; fsig = 1.929
    fstp qword[fexp] ; fexp = 6

Я ждал, чтобы найти: fsig = 123456 и fexp = -3.
Или что-то вроде: fsig = 1.23456 и fexp = 3.
Итак, что я пропустил?


person Bite Bytes    schedule 15.06.2017    source источник
comment
Обратите внимание, что процессоры работают в двоичном формате. 123.45=1.92890625*2^6.   -  person Jester    schedule 15.06.2017
comment
@Jester, спасибо, я немного изменил значение, но все равно понял   -  person Bite Bytes    schedule 15.06.2017
comment
Чтобы получить то, что вы хотите, создайте ссылку на стандартную библиотеку C, а затем выполните call ftoa :-) См. также: stackoverflow.com/questions/17632150/turn-float-into-string, stackoverflow.com/questions/22962040/ и stackoverflow.com/questions/10912128/   -  person Cody Gray    schedule 16.06.2017
comment
@Jester Хотите опубликовать это как ответ? Или вы считаете, что этот вопрос следует закрыть?   -  person Cody Gray    schedule 16.06.2017
comment
@CodyGray: ftoa - это стандартная библиотечная функция C?   -  person Michael Petch    schedule 16.06.2017
comment
О верно. Нет, это не определено стандартом. Некоторые библиотеки имеют его как расширение. Виноват.   -  person Cody Gray    schedule 16.06.2017


Ответы (1)


fxtract дает вам показатель степени 2, как сказал @Jester в комментариях.

  • 123,456 = 1,929 26

Чтобы получить основание степени 10, вы должны знать о логарифмах. Вы вычисляете логарифм по основанию 10 ввода, а затем усекаете его до целого числа. В x87 fldlg2 дает log10(x), тогда fyl2x может рассчитать логарифм как

  • log10(x) = log10(2) log2(x)

Чтобы усечь его до целого числа, вы устанавливаете x87 для округления до нуля (путем orвставки 0x0c000 в управляющее слово) и используете frndint.

Чтобы вычислить мантиссу, вы делите ввод на степень 10. Обычный способ получить степень 10 — использовать степени 2 и 5, используя целочисленная арифметика для степени 5 (как в pow5mult Дэвида М. Гея) и масштабирование экспоненты с плавающей запятой для степеней двойки. Более простой, но, возможно, более медленный или менее точный способ — использовать x87 и формулу

  • 10p = 2m = 2r 2m - r, где m = log2 10 p и r = раунд (м)

В x87 fldl2t обеспечивает log2(10). f2xm1 вычисляет 2x - 1, если x — дробь от -1 до 1. fscale умножает на 2r, если r — целое число.

Код

section .data
    fvar: dd 123.456
    fsig: dq 0.0
    fexp: dq 0.0

section .bss
    newcw: resw 1
    oldcw: resw 1

section .text
global main
main:
    fld dword[fvar]
    ;; fexp = truncate(log_10(fvar))
    fld st0
    fldlg2
    fxch st1            ; st2 = fvar, st1 = log_10(2), st0 = fvar
    fyl2x               ; log_10(fvar) = log_10(2) * log_2(fvar)
    fstcw [oldcw]
    mov dx, [oldcw]
    or  dx, 0x0c000     ; rounding mode = 3, toward zero
    mov [newcw], dx
    fldcw [newcw]
    frndint             ; truncate log_10(fvar)
    fldcw [oldcw]       ; restore old rounding mode
    fst qword[fexp]
    ;; fsig = fvar / 10^(fexp)
    fldl2t              ; st2 = fvar, st1 = fexp, st0 = log_2(10)
    fmulp               ; m = log_2(10) * fexp
    fld st0
    frndint             ; integral part of m
    fxch st1            ; st2 = fvar, st1 = integer, st0 = m
    fsub st0, st1       ; fractional part of m
    f2xm1
    fld1
    faddp               ; 2^(fraction)
    fscale              ; 10^fexp = 2^(integer) * 2^(fraction)
    fstp st1            ; st1 = fvar, st0 = 10^fexp
    fdivp               ; fvar / 10^fexp
    fstp qword[fsig]
    int 3

Я добавил метку main и int 3, чтобы запустить это в gdb на OpenBSD/amd64.

$ nasm -felf64 float10.s && gcc -nopie -o float10 float10.o 
$ gdb float10
...
(gdb) run
...
Program received signal SIGTRAP, Trace/breakpoint trap.
...
(gdb) x/1wf &fvar
0x601000 <fvar>:        123.456001
(gdb) x/1wg &fsig
0x601004 <fsig>:        1.2345600128173828
(gdb) x/1wg &fexp
0x60100c <fexp>:        2
person George Koehler    schedule 08.01.2018
comment
Если я попробую этот код (а я это сделал), fvar не может быть отрицательным. Это дает NaN на fyl2x. Чтобы расширить этот хороший пример, вы должны проверить знак перед логарифмированием. В любом случае хороший пример. - person Agguro; 14.02.2018