Пример инструкции 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,
Итак, что мне не хватает?

1 ответ

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

  • 123,456 = 1,929 × 26

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

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

Чтобы обрезать его до целого числа, вы устанавливаете x87 для округления до нуля (or0x0c000 в контрольное слово) и использовать frndint,

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

  • 10p = 2m = 2r × 2m - r, где m = log210 × p и r = округление (м)

В x87 fldl2t предоставляет журнал2(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
Другие вопросы по тегам