English Amiga Board

English Amiga Board (https://eab.abime.net/index.php)
-   Coders. Asm / Hardware (https://eab.abime.net/forumdisplay.php?f=112)
-   -   req. code for convert float to ascii (https://eab.abime.net/showthread.php?t=112947)

meynaf 24 December 2022 11:21

req. code for convert float to ascii
 
Hi,

Floating-point stuff isn't my cup of tea at all, but sometimes i find fp values in existing code and can't do much with them.
It appears the OS does not provide anything to show these.
I know C standard lib can do this, but using C is not ideal for me.

So if someone here has short, easy-to-use, accurate float display routine that does not have external dependencies, you would make someone happy :great

Note that if you wanted a new idea for a coding contest, this may be it too :D

32-bit floats should be enough for now, but other sizes are welcome too.

ross 24 December 2022 11:41

Quote:

Originally Posted by meynaf (Post 1584072)
Note that if you wanted a new idea for a coding contest, this may be it too :D

Count me off, not even for me a right cup of tea :)
But I'm curious about it.

I guess only integer instructions are accepted..

meynaf 24 December 2022 11:53

Quote:

Originally Posted by ross (Post 1584078)
I guess only integer instructions are accepted..

Yes but 68020+ is ok for me so extracting fields should be easy.

jotd 24 December 2022 11:53

you mean displaying a float as text?

I would create a simple SAS-C program, disassemble it and pick on the conversion part.

Karlos 24 December 2022 12:04

If this is for debugging rather than presentation, I'd just show them as raw hexadecimal. Plenty of tools exist that can show the IEEE interpretation of a 32 or 64-bit hexadecimal. Next up the complexity scale is showing it in decimal scientific notation but still as a power of two exponent. Generally it will always be a decimal strictly between 0 and 0.5 with some power of two exponent. Thats only about as complicated as converting integer to ASCII.

If it's just for general presentation in other contexts and you need to display it as a decimal with power of ten exponent... Well that's going to more complicated. I would probably reconsider my position on dependencies in this case.

meynaf 24 December 2022 12:29

Quote:

Originally Posted by jotd (Post 1584080)
you mean displaying a float as text?

Yes. Something that shows f.e. $3f400000 as "0.75".


Quote:

Originally Posted by jotd (Post 1584080)
I would create a simple SAS-C program, disassemble it and pick on the conversion part.

This is my fallback solution.
It's not as easy as it looks because navigating in disassembled C library code inside of a program isn't exactly straightforward. It may also have lots of options i don't need, or depend on external libraries.
Source for such C lib code would help, though.



Quote:

Originally Posted by Karlos (Post 1584086)
If this is for debugging rather than presentation, I'd just show them as raw hexadecimal.

I already have raw hex, but now i want to get something more handy and general purpose.


Quote:

Originally Posted by Karlos (Post 1584086)
Plenty of tools exist that can show the IEEE interpretation of a 32 or 64-bit hexadecimal.

Probably, but i need something easy to integrate in programs. Then it would be my library code i can reuse at will.


Quote:

Originally Posted by Karlos (Post 1584086)
Next up the complexity scale is showing it in decimal scientific notation but still as a power of two exponent. Generally it will always be a decimal strictly between 0 and 0.5 with some power of two exponent. Thats only about as complicated as converting integer to ASCII.

Currently I can only extract integer and fractional part and show them as decimal values for a coarse evaluation but it is not ideal. I lose range and accuracy.


Quote:

Originally Posted by Karlos (Post 1584086)
If it's just for general presentation in other contexts and you need to display it as a decimal with power of ten exponent... Well that's going to more complicated. I would probably reconsider my position on dependencies in this case.

Ideally this is what i want, possibly with a few formatting options. :spin

But hey, if it was easy, i'd just have done it and not opened a thread here ;)

Thomas Richter 24 December 2022 12:43

Quote:

Originally Posted by meynaf (Post 1584072)
Floating-point stuff isn't my cup of tea at all, but sometimes i find fp values in existing code and can't do much with them.
It appears the OS does not provide anything to show these.
I know C standard lib can do this, but using C is not ideal for me.

So if someone here has short, easy-to-use, accurate float display routine that does not have external dependencies, you would make someone happy :great

That's right, the Os does not include such functions, for reasons beyond my understanding.


You find functions here:


http://aminet.net/dev/lib/ThorLib.lha


This is a link library containing a couple of support functions, in particular converting between various floating point types and ASCII.


Link library means that only small segments of the entire library appear in your binary, not the whole thing.


In particular, in thor/conversions.h, you find the following:


Code:


void __regargs XToA(extendedfloat in,char *s);

which converts an extended precision (80 bit) number to ASCII, and you also find


Code:

void __regargs SToX(shortfloat *in,extendedfloat out);
which converts a 32 bit single precision (mathieeesingbas) number to extended precision.


With those two, you get the functionality you need.


There are also some lower level functions that give you a bit more freedom in formatting.

meynaf 24 December 2022 13:39

Quote:

Originally Posted by Thomas Richter (Post 1584100)
You find functions here:

http://aminet.net/dev/lib/ThorLib.lha


This is a link library containing a couple of support functions, in particular converting between various floating point types and ASCII.

This is more or less the same as what jotd suggested...

Is source code available ?


Quote:

Originally Posted by Thomas Richter (Post 1584100)
Code:

void __regargs XToA(extendedfloat in,char *s);

What about the size of the output buffer ?

Thomas Richter 24 December 2022 14:05

Quote:

Originally Posted by meynaf (Post 1584117)
Is source code available ?

I'm wondering... are you interested in understanding how this conversion works? In such case, the source code is only partially interesting because it's mostly on mathematics and not so on the code which contains some "magic constants" that are not immediately obvious. If so, I can provide you the details, and you can then implement it yourself.


If you're only looking for a function that is doing its job, then you would not need sources. Just link to it, and you're be happy.



It's BTW assembler, there is no really good way to do this conversion from C.


Quote:

Originally Posted by meynaf (Post 1584117)
What about the size of the output buffer ?


32 bytes should be enough. The precision is 16 digits if I recall, plus the exponent.

meynaf 24 December 2022 14:21

Quote:

Originally Posted by Thomas Richter (Post 1584119)
I'm wondering... are you interested in understanding how this conversion works? In such case, the source code is only partially interesting because it's mostly on mathematics and not so on the code which contains some "magic constants" that are not immediately obvious. If so, I can provide you the details, and you can then implement it yourself.

Yes, the details would be fine.
Ideally the method must not use floats itself, just extract exponent and mantissa (which is easy) then do its job on integers.

Thomas Richter 24 December 2022 14:44

Ok then, I'll walk you through. It will be a series of posts then because the entire thing is a bit too long to fit into a single post (plus, it will not be very helpful in this form). Yes, it is surely all integer, it does not need any of the math libraries and neither the FPU. The math libraries do not provide you with the precision I wanted, and the FPU may not be available.

The general outline is the following: First, the main function AToX() is a small wrapper around a lower level function which extracts the sign, the decimal exponent of the number in binary form, and the mantissa as a sequence of ASCII digits. From that, one can clearly build the desired string by placing the decimal dot in the right place and adjusting the exponent, plus some "optical rounding" if desired. This function also covers some special cases such as 0, +INF, -INF and the NANs.

The lower levels are more interesting and I will provide some snippets one after another.

First step is to extract from the floating point number an approximation of the decadic logarithm of the number. This takes the binary exponent and the leading bits of the mantissa to compute an approximation of log10(input). This is where the "magic constants" are. This is an approximation that is fixed up later.

Second step is to normalize the input such that the mantissa comes between 0.1 and 1. This takes the log10() from the first step, then runs into a multiplication chain which multiplies the number by powers of 10 of the form 10^(2^n), using the binary expansion of the exponent from the first step. That is the long but algorithmically not so complex part.

Third step is to perform some rounding and normalization of the resulting mantissa, i.e. to ensure that it is really between 0.1 and 1, plus fixup of the exponent from the first step.

The last step is just continuously multiplying the extracted mantissa by 10, and always removing the next decimal digit from its integer part, storing it in the output buffer. That is also fairly trivial.

The entire conversion, however, goes over several subroutines, so it is a bit lengthy. I'll start with the next post with the first step.

Jobbo 24 December 2022 16:32

Why not just convert to FFP and then use fpa to convert?

http://amigadev.elowar.com/read/ADCD.../node015B.html

Edit: it's fpa not fta.

Thomas Richter 24 December 2022 17:43

Here is the first part of the job. Creating a normalized representation.

Code:

;;; **************************************************************
;;; ** IntLog10                                                        **
;;; ** Compute an estimate of log_10 of (a0) and return in d0        **
;;; ** Adjust (a0) such that it is between 0 and 1                **
;;; ** (a0) must be normalized and neither negative, zero,        **
;;; ** INF nor NAN                                                **
;;; ** The result may be a little bit too large, but                **
;;; ** consistent with the integer result, i.e. 10^d0 * (a0)        **
;;; ** is the original number up to rounding                        **
;;; **************************************************************
        xdef        IntLog10
IntLog10:
        saveregs d2-d3
       
        move.w        (a0),d0                ;get exponent
        move.l        d0,d1
        mulu.w        #$4d10,d0        ;multiply with log_10(2) x 2^16 -> convert to decadic logarithm
        lsr.w        #8,d1
        mulu.w        #$4d,d1
        add.l        d1,d0                ;this multiplies the exponent with 4d10.4d, which is log_10(2)
        ;; note that there is still an offset of 0x3fff on the exponent, which
        ;; accounts for an offset of 1343C622 = 4d10.4d * 3fff in the product that need to be removed
        moveq        #0,d1
        move.b        4(a0),d1        ;get topmost digit
        mulu.w        #$9a,d1                ;approximate the log linearly (note that 0x80 represents 1.0)
        ;; the above multiplies (1+mantissa) with 0x4d which is approximately log_2(10)
        ;; thus, the result is by $80x$9a = 0x4d00 too large as log(1+x) = x for small x.
        add.l        d1,d0
        sub.l        #$134312f4,d0        ;remove offset
        clr.w        d0
        swap        d0                ;this is an approximation of log(10) of the number that is by 1 too large
        ;; this is necessary as we need a number < 1 as result.
        move.l        d0,d2                ;keep at the moment
        neg.w        d0
        bsr        TimesIntPowTen        ;normalize the number to be in [0.1,1).

        move.l        d2,d0
        loadregs
        rts

This code I actually took from SoftIEEE because I added the comments there, but it performs the same function. The contents of (a0) are a positive extended precision number. The function up in the call stack removes the sign and filtered already 0, INF and NAN out and converted them right away.

The purpose of the function above is to normalize the input number to one that is between 1 and 0.1, and an approximation of the decimal exponent is returned in d0.

More on TimesIntPowTen later, even though the function itself is rather trivial.

Bruce Abbott 25 December 2022 01:12

1 Attachment(s)
Quote:

Originally Posted by meynaf (Post 1584095)
It's not as easy as it looks because navigating in disassembled C library code inside of a program isn't exactly straightforward. It may also have lots of options i don't need, or depend on external libraries.
Source for such C lib code would help, though.

Here's the code I use in my disassember, loosely based on a disassembly of SASC printf.

The main 32 bit routine is shown below. Complete source code for single/double/extended formats is attached.

Code:

FPDIGITS set 6                  ; number of decimal digits to show
FPROUND = 1

;--------------------------------
;  Show Floating Point Single
;--------------------------------
;
; in: D0 = 32 bit single
;    a3 = text output buffer
;
;out: d0 = 0,z if decimal, -1,nz if Infinity or Nan
;    a3 = end of text in buffer
;
show_single:
 movem.l A2/D2-D7,-(SP)
 bclr    #31,D0                ; clear sign bit
 beq.s  .done_sign
 move.b  #'-',(A3)+            ; was negative so print '-'
.done_sign:
 move.l  D0,D2
 bne.s  .not_zero
; --- number = 0 ---
 move.b  #'0',(A3)+
 move.b  #'.',(A3)+            ; print '0.'
 moveq  #FPDIGITS-3,D0
.trailing:
 move.b  #'0',(A3)+            ; print trailing '0's
 dbf    D0,.trailing
 bra    .retz                  ; return z = rational number
; --- not zero ---
.not_zero:
 andi.l  #$007FFFFF,d0          ; D0 = 23 bit mantissa
 swap    d2
 andi.w  #$7F80,d2
 lsr.w  #7,d2                  ; D2 = 8 bit exponent
 cmpi.w  #255,d2
 bne.s  .rational
; -- infinity or NAN ---
 tst.l  D0                    ; if mantissa = 0  then
 bne.s  .nan
 bsr    show_infinity          ;  show 'INFINITY'
 bra.s  .notnum
.nan:                          ; else not a number so
 bsr    show_nan              ;  show 'NAN'
.notnum:
 moveq  #-1,d0                ; return -1 = Infinity or NaN
 bra    .done
; --- rational number ---
.rational:
 bset    #23,D0                ; set bit 23, mantissa now (binary) 1.xxx...
 subi.w  #127,D2                ; convert to signed integer -127 to +127
 addq.w  #1,D2                  ; exponent +1, 1.xxx will scale down to 0.1xxx
; --- convert exponent to decimal ---
 move.w  D2,D5                  ; D5 = binary exponent
 moveq  #0,D7                  ; decimal exponent = 0
; if exponent <> 0 then scale number up/down until it is
.scale:
; andi.l  #$00FFFFFF,d0        ; remove possible overflow from mantissa
 move.w  D5,D2                  ; d2 = binary exponent
 beq.s  .bin2dec              ; if zero then don't need to scale
 bpl.s  .scale_down            ; if > zero then scale down
; negative exponent            ; < zero so scale up
 addq.w  #3,D2                  ; d2 = exponent + 3
 bmi.s  .scale_up              ; if still < zero then scale up * 10
; small negative exponent
 eori.w  #$0003,D2              ; 0..2 -> 3..1
 add.w  D2,D5                  ; binary exponent + 3..1
 lsr.l  D2,D0                  ; mantissa / 2..8
 bra.s  .bin2dec              ; binary exponent now zero so done scaling
; positive exponent
.scale_down:
 bsr    _div32x10              ; mantissa / 10
 addq.w  #1,D7                  ; decimal exponent + 1
 bra.s  .normalize            ; binary normalize
; large negative exponent
.scale_up:
 moveq  #4,D2
 add.w  D2,D5                  ; binary exponent + 4
 lsr.l  D2,D0                  ; mantissa / 16
 bsr    _mul24x10              ; mantissa * 10
 subq.w  #1,D7                  ; decimal exponent - 1
.normalize:
 cmpi.l  #$007fffff,d0          ; mantissa = (binary) > 1.xxx... ?
 bhi.s  .scale                ; yes, scale it down
 subq.w  #1,D5                  ; binary exponent - 1
 lsl.l  #1,D0                  ; mantissa * 2
 bra.s  .normalize            ; continue normalizing
;
; --- convert normalized mantissa to decimal string
.bin2dec:
 move.l  a3,a0
 moveq  #FPDIGITS+1,D2        ; D2 = number of digits to generate
 moveq  #0,d1
 bra.s  .convert_digit
.b2d_loop:
 moveq  #0,d1                  ; d1 = 0
 andi.l  #$00FFFFFF,d0          ; remove overflow
 beq.s  .convert_digit        ; if number = 0 then digit = 0
 bsr    _mul24x10              ; d0 = number * 10
 move.l  d0,d1
 swap    d1
 lsr.w  #8,d1                  ; d1 = overflow
.convert_digit:
 addi.b  #'0',D1                ; convert to ascii '0'..'9'
 move.b  D1,(a0)+              ; output digit
 dbf    D2,.b2d_loop          ; loop until done all digits
;--- adjust fractional result
 cmp.w  #-37,d7                ; if decimal exponent > -37
 ble.s  .round
 cmpi.b  #'0',1(a3)            ; and mantissa = 0.xxxxxx...
 bne.s  .round
 lea    2(a3),a0
 lea    1(a3),a1
 moveq  #FPDIGITS-1,d2
.x10:
 move.b  (a0)+,(a1)+            ; change it to x.xxxxx...
 dbf    d2,.x10
 subq.w  #1,d7                  ; and adjust decimal exponent
; -- round up decimal number
.round:
 ifd FPROUND
 moveq  #FPDIGITS-1,d2      ; D2 = number of digits to round
 lea    FPDIGITS(a3),a0
 move.b  (a0),d0            ; get last generated digit
 addq.b  #1,d0              ; round up
.round_loop:
 cmpi.b  #'9',d0
 ble.s  .rounded            ; if < 10 then done rounding
 moveq  #'0',d0
 move.b  d0,(a0)            ; change digit to '0'
 move.b  -(a0),d0            ; get previous digit
 addq.b  #1,d0              ; round up
 move.b  d0,(a0)            ; store rounded digit
 dbf    d2,.round_loop
 addq.w  #1,d7              ; rounded up to msd, mantissa is now 10.0
 bra.s  .decimal_point
.rounded:
 endif
 move.b  1(a3),(a3)          ; move 2nd digit to 1st digit
.decimal_point:
 move.b  #'.',1(a3)          ; insert '.' after 1st digit
.trim:
 lea    FPDIGITS(a3),a3    ; move to end of signifant digits
 moveq  #FPDIGITS-3,d2      ; d2 = count to leave 1 zero after dp
.trim_zeros:
 cmpi.b  #'0',(a3)
 bne.s  .prt_exponent
 subq.l  #1,a3              ; remove excess trailing zeros
 dbf    d2,.trim_zeros
 addq.l  #1,a3              ; skip last digit
.prt_exponent:
 subq.w  #1,D7              ; decimal exponent - 1
 move.w  D7,D0              ; decimal exponent = 0 ?
 beq.s  .retz              ; yes, done
 move.b  #'E',(a3)+          ; print 'E'
 bsr    show_int16          ; print signed decimal exponent
.retz:
 moveq  #0,d0              ; rational number (not infinity or NaN)
.done:
 movem.l (sp)+,a2/d2-d7
 rts

Note that while this code seems to work OK I haven't thoroughly tested it. Accuracy is not as good as I would like, and some 'corner' cases are not handled well. Floor and ceiling values for 64 and 80 bit format are hard coded because they take too long to calculate. Rounding is a bit iffy. I don't even try to handle packed decimals or subnormals.

Getting floating point 'right' under all circumstances is surprisingly difficult. I recently discovered that the assembler I use doesn't produce correct floating point numbers, so I can't even disassemble some code and then reassemble it to check for correct conversion. There are lots of online converters with inscrutable code that assume you have a particular environment (javascript, Intel fpu etc.) whose accuracy cannot be verified. I am not by any means an expert on floating point formats. However when several online calculators can't even agree (let alone show proof that their algorithms are correct) it doesn't inspire confidence.

Thorham 25 December 2022 09:10

Good lord, what an absolute pain :scream Quite the opposite of converting integers to ASCII :rolleyes

Thomas Richter 25 December 2022 12:16

Next function in row is TimesIntPowTen, which multiplies a number with a power of 10. This is required to get the mantissa normalized to the interval [0.1,1] (or mostly), given the log10() approximation from yesterday's post.

Code:

;;; **************************************************************
;;; ** TimesIntPowTen                                                **
;;; ** Multiply (a0) with 10^d0 and return in (a0)                **
;;; **************************************************************
        xdef        TimesIntPowTen
TimesIntPowTen:
        saveregs d2-d3/a2
        tst.w        d0                ;negative?
        bmi.s        .invpower
        lea        PowTenTable(pc),a2
        bra.s        .multiply
.invpower:
        neg.w        d0                      ;compute inverse powers here
        lea        InvPowTen(pc),a2
.multiply:
        move.l        d0,d2
        moveq        #13-1,d3
.loop
        lsr.w        #1,d2                ;test next digit
        bcc.s        .nomult
        move.l a2,a1
        bsr ExtMul
        tst.w        d2
.nomult:       
        lea $c(a2),a2                ;next power
        dbeq d3,.loop
.exit:
        loadregs
        rts

ExtMul is an extended precision multiplication in 80 bits. It is not very interesting, it just multiplies the two 64 bit mantissas and adds the exponents, and fixes the exponent offset. I believe I can leave that to the reader.

The interesting part here is that I do not use a continuous multiplication by 10 (or 1/10) for normalization because that would just accumulate errors. Instead, an arbitrary power of 10 (10^n) is split up into multiplications with powers of 10 to powers of 2, i.e. write

Code:

10^n = 10^(\sum_i b_i 2^i)
where b_i is either 0 or 1, which is here in the carry after shifting n in register d2 to the right.

The tables PowTenTable and InvPowTen contain numbers of the form 10^(2^i) with i from 1 to 12 and 10^(-2^i) also from i=1 to 12, corresponding to the range of extended precision. It is probably no coincidence that the 68882 contains the same numbers in its constant ROM, probably for exactly the same purpose.

To be continued...

Thomas Richter 25 December 2022 12:23

Quote:

Originally Posted by Thorham (Post 1584269)
Good lord, what an absolute pain :scream Quite the opposite of converting integers to ASCII :rolleyes


Yes, it is unfortunately quite involved, expecially if you want to keep the precision high and waste as little bits as possible. For double precision input (and this was the target development of my functions) you need extended precision to avoid "wasting precision". For extended precision, you would again need some extra bits, but even the 68882 does not do that and has only a precision of ~8ulp for its extended to packed decimal conversion. This conversion function is almost the same as the conversion to ASCII.


I still wonder why CBM did not include that in mathieeedoubtrans. The latter library is more or less a direct copy from the Greenhill C compiler CBM used back then (and for the compilation of intuition up to 3.1) until they switched to lattice (which then became SAS/C). The Greenhill C math library surely included something similar, but the function is probably poor to isolate since it requires several subroutines (as mine does).



The amiga.lib fpa function is rather low quality (along with a lot of other stuff in that library) and not very precise.


There is something similar in Aztec C, but their implementation is comparibly poor and of low precision. SAS/C is ok mathematically, the only thing I did not like is that they perform some "optical rounding", i.e. their function aims at generating "nice" numbers such that 0.9999999 is displayed as 1.0, even though it is not 1.0. I did not like that.

Thorham 25 December 2022 13:47

Quote:

Originally Posted by Thomas Richter (Post 1584302)
I still wonder why CBM did not include that in mathieeedoubtrans. The latter library is more or less a direct copy from the Greenhill C compiler CBM used back then (and for the compilation of intuition up to 3.1) until they switched to lattice (which then became SAS/C). The Greenhill C math library surely included something similar, but the function is probably poor to isolate since it requires several subroutines (as mine does).

The amiga.lib fpa function is rather low quality (along with a lot of other stuff in that library) and not very precise.

There is something similar in Aztec C, but their implementation is comparibly poor and of low precision. SAS/C is ok mathematically, the only thing I did not like is that they perform some "optical rounding", i.e. their function aims at generating "nice" numbers such that 0.9999999 is displayed as 1.0, even though it is not 1.0. I did not like that.

There's the old Cephes software floating point library. Has libraries for single precision all the way up to 336 bit floats: https://netlib.org/cephes/ (I use those 336 bit floats with Lua on my A1200 as a calculator)

meynaf 26 December 2022 07:39

Quote:

Originally Posted by Bruce Abbott (Post 1584243)
Here's the code I use in my disassember, loosely based on a disassembly of SASC printf.

The main 32 bit routine is shown below. Complete source code for single/double/extended formats is attached.

Yes, this is what i asked for. Thanks.


Quote:

Originally Posted by Bruce Abbott (Post 1584243)
Getting floating point 'right' under all circumstances is surprisingly difficult. I recently discovered that the assembler I use doesn't produce correct floating point numbers, so I can't even disassemble some code and then reassemble it to check for correct conversion. There are lots of online converters with inscrutable code that assume you have a particular environment (javascript, Intel fpu etc.) whose accuracy cannot be verified. I am not by any means an expert on floating point formats. However when several online calculators can't even agree (let alone show proof that their algorithms are correct) it doesn't inspire confidence.

This is because it can not really be 'right'. Contrary to integer, float does not provide exactness. This is the main reason why i don't use floats whenever i can do otherwise.
My goal here is to get something that does a 'good enough' job (i.e. is at worse slightly off but never completely wrong).



Quote:

Originally Posted by Thomas Richter (Post 1584302)
SAS/C is ok mathematically, the only thing I did not like is that they perform some "optical rounding", i.e. their function aims at generating "nice" numbers such that 0.9999999 is displayed as 1.0, even though it is not 1.0. I did not like that.

Perhaps this can be made an option.
Sure that showing 0.9999999 as 1.0 is wrong, but then what about 0.1 ? Without a little rounding, it is never shown as 0.1 as this number can not be represented exactly with a binary float.

Thomas Richter 26 December 2022 11:04

So here comes the rest. The remaining code removes the remaining binary exponent, then adds 0.5ulp of the output precision, and then uses a long shift register, multiplying the mantissa with 10 each times and copying the integer part to the target buffer.

It expects the floating point number in a0 and generates results in a1, the number of digits in d0.
Code:

        saveregs d2-d7/a2-a6
        move.l a0,a3
        move.l a1,a5
        move.l d0,a6
        move.w  (a3),d2                ;get exponent
        sub.w #$3ffe,d2                ;remove exponent bias
        movem.l 4(a3),d6-d7          ;get mantissa
        neg.w  d2                     
        moveq  #0,d4                  ;remove remaining binary exponent
        ; d4 are the remaining fractional bits that are shifted off to the right
        for.s
        lsr.l  #1,d6
        roxr.l #1,d7
        roxr.w #1,d4
        next d2                       
        ; now add up 0.5ulp to the target precision
        ; if less than 16 digits output precision are
        ; required, larger constants are needed
        add.w  #$5600,d4             
        move.l  #$39a,d2
        addx.l  d2,d7
        moveq  #0,d2
        addx.l  d2,d6                  ;include carry

        move.l  a6,d2                  ;# digits->d2
        subq.w  #1,d2
        ;; now scale up by ten, and move the integer part to
        ;; the output buffer
.do:
        moveq #0,d0
        move.l d6,a1
        move.l d7,a2
        move.w d4,d5                 

        lsl.w  #1,d4
        roxl.l #1,d7
        roxl.l #1,d6
        roxl.b #1,d0                  ;*2

        lsl.w  #1,d4
        roxl.l #1,d7
        roxl.l #1,d6
        roxl.b #1,d0                  ;*4

        add.w  d5,d4
        move.l a2,d5
        addx.l d5,d7
        move.l a1,d5
        addx.l d5,d6
        moveq #0,d3
        addx.w d3,d0                  ;+1=*5

        lsl.w  #1,d4
        roxl.l #1,d7
        roxl.l #1,d6
        roxl.b #1,d0                  ;*2=*10

        or.b #'0',d0                    ;strip off the integer part
        move.b d0,(a5)+            ;place in the result
        dbra d2,.do

        loadregs
        rts

...and that it is. All you need to do now is to take the integer exponent from the first step, from that compute where the decimal dot in the output buffer is, and "format" the mantissa generated by the above function according to your likes. If you want to, you can include some "optical rounding" there.
The higher level function for that is again not particularly interesting, it is pretty much boilerplate code and does not require assembly.


All times are GMT +2. The time now is 04:24.

Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.

Page generated in 0.05045 seconds with 11 queries