Пример инструкции 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 для округления до нуля (or
0x0c000 в контрольное слово) и использовать 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