2

I wrote this code in 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

I was waiting to find: fsig = 123456 and fexp = -3.
Or something like: fsig = 1.23456 and fexp = 3.
So, what Am I missing?

Cody Gray
  • 230,875
  • 49
  • 477
  • 553
Bite Bytes
  • 1,365
  • 6
  • 21
  • 5
    Note that processors work in binary. `123.45=1.92890625*2^6`. – Jester Jun 15 '17 at 16:00
  • 1
    @Jester thank's, I changed the value slightly, but I get it anyway – Bite Bytes Jun 15 '17 at 16:01
  • 1
    To get what you want, link to a C standard library and then do `call ftoa` :-) See also: https://stackoverflow.com/questions/17632150/turn-float-into-string, https://stackoverflow.com/questions/22962040/easiest-way-to-convert-a-decimal-float-to-bit-representation-manually-based-on-i, and https://stackoverflow.com/questions/10912128/printing-the-integral-part-of-a-floating-point-number – Cody Gray Jun 16 '17 at 10:54
  • @Jester Would you like to post that as an answer? Or do you think this question should be closed? – Cody Gray Jun 16 '17 at 10:55
  • @CodyGray : `ftoa` is a standard C library function? – Michael Petch Jun 16 '17 at 13:29
  • Oh, right. No, it's not defined by the standard. Some libraries have it as an extension. My bad. – Cody Gray Jun 16 '17 at 14:43

1 Answers1

3

fxtract gives you the exponent base 2, as @Jester said in the comments.

  • 123.456 = 1.929 × 26

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 oring 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
George Koehler
  • 1,450
  • 16
  • 22
  • If I try this code (and I did) fvar can't be negative. It gives NaN on fyl2x. To extend this nice example you should check the sign before taking logarithms. Anyway nice example. – Agguro Feb 14 '18 at 06:51