fxtract
gives you the exponent base 2, as @Jester said in the comments.
To get the exponent base 10, you must know about logarithms. You calculate the logarithm base 10 of the input, then truncate it to an integer. In x87, fldlg2
gives log10(x), then fyl2x
can calculate the logarithm as
- log10(x) = log10(2) × log2(x)
To truncate it to an integer, you set the x87 to round toward zero (by or
ing 0x0c000 into the control word) and use frndint
.
To calculate the significand, you divide the input by the power of 10. The customary way to get a power of 10 is to use powers of 2 and 5, using integer arithmetic for powers of 5 (as in David M. Gay's pow5mult), and scaling the floating-point exponent for powers of 2. A simpler, but perhaps slower or less accurate way, is to use the x87 and the formula
- 10p = 2m = 2r × 2m - r where m = log210 × p and r = round(m)
In x87, fldl2t
provides log2(10). f2xm1
calculates 2x - 1 if x is a fraction between -1 and 1. fscale
multiplies by 2r if r is an integer.
The code is
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
I have added the label main
and the int 3
so I can run this in gdb on 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