Searching \ for 'floating point routines' in subject line. () Help us get a faster server
FAQ page: www.piclist.com/techref/index.htm?key=floating+point+routines
Search entire site for: 'floating point routines'.

Truncated match.
'Floating Point Routines'
1995\10\17@034600 by   Hi
I was wondering if anyone had got the FP routines that are published
in the Embedded Control Handbook to work properly.  I am havinf
extreme hassels when I have to deal with the value "zero".  The code
more importantly, has anyone corrected this problem.

Norman  all,

Norman wrote:

>I was wondering if anyone had got the FP routines that are published
>in the Embedded Control Handbook to work properly.  I am havinf
>extreme hassels when I have to deal with the value "zero".

I have not looked at the specific routines you mentioned, but in general
zero is a miserable number to deal with in floating point. In 1990 I
wrote an IEEE 10 byte floating point math suite. It's been a long time
so the details are a little sketchy.

First of all understand that floating point is nothing but an approximation.
This means that every single fraction between any two numbers is not
representable. Due to this you run into problems. As you begin subtracting
.00000001 from .01 (say in a tight loop) you begin to approach zero from
the positive side. eventually the result will underflow and your result
will be POSITIVE ZERO (+0). This is literally how the number is represented
internally. The same thing can happen from the negative side of zero
resulting in NEGATIVE ZERO (-0)

When underflow occurs one of two things can happen 1) you get an underflow
exception error and processing stops or 2) if the exception is masked a
PSUEDO ZERO is returned. (this is where it gets sketchy...) Psuedo Zero's
break down into 2 categories: UN-NORMAL (typically intermediate terms
that have not been normalized yet, I think) and DE-NORMAL (the result of
an operation that is known to be incorrect, verrry sketchy, maybe the
result of an operation on 2 un-normals. I do remember that if you continue
to do math with de-normals you get NAN, Not A Number). Un-normal and
De-normal can be positive or negative in sign.  Anyway, the key thing
here is that unless you are doing some strange math, (where you will
need to know the level of degredation of your result) these are all
considered zero. Trouble is they all have different representations.
Un-Normals have an exponent of 0x0001 (+Un-normal) and 0x8001 (-Un-normal)
De-Normals have an exponent of 0x0000 (+De-normal) and 0x8000 (-De-normal)

Probably the greatest problem is created by roundoff and rounding errors
There are 3 types of rounding: up, down, and nearest. Each of these will
effect the result of operations on any two numbers. the problem is that
you get 1 or 2 bit rounding errors all the time, no matter what you do
Remember this is just an approximation gaurenteed to a specific number
of significant digits. Ususlly there is 2-3 bits left over that are not
part of the "significant-ness" of the number, but the influence partial
results. (dont try this *exact* example, I'm faking it)

assume you have a system gaurenteed for 8 significant digits
For example .00000005 - .00000004 should equal .00000001 right?

but .00000005 cant be represented exactly in FP so it's actually
.00000005013 and when you subtract them you get .00000001013 which is
accurate to 8 digits. now you subtract .00000001 from that number
and you get .00000000013 (which is zero from the standpoint of 8
significant digits) but internally this is being compared to .00000000000
and they are not equal.

this is why generally you never say:

if( float1 = float2 ) ...

you normally say

if( abs(float1 - float2) > theta ) ...

where theta is generally the smallest representable number (or
some other "magic" tollerance value)

needless to say zero is a paticularly nasty number in floating point

sorry this is so long winded. Hope it helps

Michael J. Schreck   Mike Schreck <schreck HORIZSYS.COM> wrote:

>.... eventually the result will underflow and your result
>will be POSITIVE ZERO (+0). This is literally how the number is represented
>internally. The same thing can happen from the negative side of zero
>resulting in NEGATIVE ZERO (-0)
> ....
>Trouble is they all have different representations.
>Un-Normals have an exponent of 0x0001 (+Un-normal) and 0x8001 (-Un-normal)
>De-Normals have an exponent of 0x0000 (+De-normal) and 0x8000 (-De-normal)
>
>needless to say zero is a paticularly nasty number in floating point

Which is why the representation used by Microchip's floating-point
appnotes is specifically tailored to produce A UNIQUE REPRESENTATION of
zero.  The guy who posted the original message in this thread should

-Andy

--
Andrew Warren - fastfwd ix.netcom.com
Fast Forward Engineering, Vista, California   PIC'ers

The guy (me) that originally posted this message read the AppNote so
carefully it's painful - unfortunately, the code does not work
correctly (even admitted by the author) and discovered by others that
have tried it.  The zero does not give correct answer when used
especially with the ADD and SUBTRACT routines. I have written some
code to overcome the problem - but it's a little messy as I'm pretty
restricted as far as time goes. Any way, thanks to all that replied.

Norman

>     Which is why the representation used by Microchip's floating-point
>     appnotes is specifically tailored to produce A UNIQUE REPRESENTATION of
>     zero.  The guy who posted the original message in this thread should
>     read the appnote documentation carefully.
>
>     -Andy   Norman Fick <NFICK ELECENG.UCT.AC.ZA> wrote:

>The guy (me) that originally posted this message read the AppNote so
>carefully it's painful - unfortunately, the code does not work
>correctly (even admitted by the author) and discovered by others that
>have tried it.

Sorry, Norman... I was unaware of the bugs in the appnote.

-Andy

--
Andrew Warren - fastfwd ix.netcom.com
Fast Forward Engineering, Vista, California

'floating point routines'
1998\02\16@075939 by  To all,

My previous message might have been unclear as stated.  What I am
looking for is a method of converting a 16 bit ADC value into an
appropriate voltage value (ie. 20 V * (ADC value/65535).  I feel certain
that I need to use floating point methods, but have little experience in
using them.  Can anyone help?

Aaron   Aaron Hickman <PICLIST MITVMA.MIT.EDU> wrote:

> What I am looking for is a method of converting a 16 bit ADC value
> into an appropriate voltage value (ie. 20 V * (ADC value/65535).  I
> feel certain that I need to use floating point methods, but have
> little experience in using them.  Can anyone help?

Aaron:

You neither need nor want floating-point math for this.

As I recall from your previous message, you want to send an ASCII
reprsentation of the voltage (in the range [-10 to +10] to a display.

Is that correct?  And if so, how many digits do you want to display?

If you just want to display one digit to the right of the decimal
point, it's real easy:

1.  Find the absolute value of your 16-bit number:  If the
hi-bit of the number is set, simply clear that bit; if the
hi-bit is clear, subtract the 16-bit number from 0x7FFF.

This will leave you with a number in the range [0-32767].
Remember whether your original number was positive (hi-bit
set) or negative (hi-bit clear).

To convert from [0-32767] to [0-10], you COULD divide by
3276.7 (a floating-point number), which would almost
certainly give you a non-integer result.

We don't need a lot of precision, though, so we could just
divide by 3277; it's close enough for our purposes.

However, that would still give us a non-integer result, and
we don't want that.

So... We'll do an integer divide by 328, which will give us
an integer in the range [0-100] (or [0-99], if we don't
round our result to the nearest integer), and we'll just
place the decimal point between the two least-significant
digits of that result to get a FINAL result in the range
[0.0 - 10.0] (or [0.0 - 9.9]).

Make sense?

Ok... We'll just make one more optimization:  Instead of
dividing by 328 (which is inconvenient, since it's too large
to fit in 8 bits), we'll divide our dividend by 2 (so it'll
be in the range [0-16383] instead of [0-32767]), then we'll
divide it by 164 instead of 328.

2.  Divide the result of Step 1 by 2 (by shifting it right one
position).  This is so we can use a 16/8 division routine in
the next step.

3.  Take the result from Step 2 and divide it by 164, using the
simplest 16-bit / 8-bit integer division routine you can
find.

The result will be the absolute value of the voltage * 10.

4.  Display that number, putting a decimal point between the
front of the number.

The whole thing should take WAY less than a page of code.

-Andy

=== Andrew Warren - fastfwd ix.netcom.com
=== Fast Forward Engineering - Vista, California
=== http://www.geocities.com/SiliconValley/2499  Aaron Hickman <hickmaab DUNX1.OCS.DREXEL.EDU> wrote:
>To all,
>
>        My previous message might have been unclear as stated.  What I am
>looking for is a method of converting a 16 bit ADC value into an
>appropriate voltage value (ie. 20 V * (ADC value/65535).  I feel certain
>that I need to use floating point methods, but have little experience in
>using them.  Can anyone help?

Aaron,

First of all, the maximum input to your A/D is probably equal
to the reference voltage, typically 2.5 or 5.  Second, your
question implies a range of 20 volts.

You will need a voltge divider with a divisor of F.  For example,
if you used 10K series and 75K shunt, F = (10+47)/10 = 8.5

;The input in millivolts to the a/d for a 20V input is:
; 10 * 20000 / 85.  Note X10 in numerator and denominator
;Change the divisor here if the resistor voltage divider changes.

max_a2d_input    equ 10 * 20000 / 85   ; in mv

;Assuming a count of 65535 (16 bit converter) for 2500 mv (Vref) in:

count_for_20     equ 2500 * 65535 / max_a2d_input

counts_per_volt  equ       count_for_20 / 20

Now, given an A/D count, the voltage is   count/counts_per_volt

Using the above numbers:

mas_a2d_input =  2.35 mv
count_for_20 = 27887
counts_per_volt = 1394

Most of these calculations are done at compile time.  To convert an
A/D count to volts you only need one division which does not have to
be floating point.  The following routine will do the trick:

;*******************************************************************
;  Division:  Divide 16 bits by 16 bits,
;             16 bit quotient and remainder.
;  Standard shift and subtract algorithm.
;
;  q_2:q_1 / n_2:n_1 -> q_2:q_1   Remainder in t_2:t_1
;
;  Uses count
;
;  Execution time: 271 to 361 clock cycles
;  Program memory: 27
;
;  (This is a cleaned up version of the routine in AN526)
;
;*******************************************************************

div16b16: macro
local  loop, check_sign, check_count
movlw   16
movwf   count
clrf    t_2
clrf    t_1
div16_loop:
rlf     q_1, f
rlf     q_2, f
rlf     t_1, f
rlf     t_2, f
movf    n_2, w
subwf   t_2, w
skpz
goto    check_sign
movf    n_1,w
subwf   t_1,w
check_sign:
skpc
goto    check_count
movf    n_1,w
subwf   t_1, f
skpc
decf    t_2, f
movf    n_2,w
subwf   t_2, f
setc
check_count:
decfsz  count, f
goto    loop
rlf     q_1, f
rlf     q_2, f

endm

--
Bob Fehrenbach    Wauwatosa, WI     bfehrenb execpc.com  On Mon, 16 Feb 1998 07:55:47 -0500 Aaron Hickman
<hickmaab DUNX1.OCS.DREXEL.EDU> writes:
>To all,
>
>        My previous message might have been unclear as stated.  What I
>am
>looking for is a method of converting a 16 bit ADC value into an
>appropriate voltage value (ie. 20 V * (ADC value/65535).

Writing the conversion this way is an important clue about how to
proceed.  Integer math can be used throughout.  First scale the entire
process by how many digits you want to display after the decimal.  The
16-bit converter gives about 5 (decimal) significant digits, so you
probably want to show the voltage in the form xx.xxx.  To do this,
compute voltage * 1000 as a big integer, then insert the decimal point
later.

The formula for (voltage * 1000) is ADC value * (20000 / 65536). Binary
multiplication is generally faster and easier than division, so I use the
"odd" factor (20000) in the multiplication.  The division by 65536 is a
special case easily accomplished by shifting the result 16 bits to the
right (discarding the low 2 bytes).  Either a specially optimized routine
to multiply by 20000 or general 16*16 bit multiply routine that gives a
32 bit result could be used.  After multiplying, take only the high 2
bytes (divide by 65536) and convert to decimal.  There may be some use in
looking at the MSB of the discarded bytes and using it to improve
rounding.

You didn't say how the sign is handled.  If the ADC outputs 32768 for a
zero voltage input, one easy way would be to subtract 10000 from the
result just before converting to decimal (leaving 0 if the input voltage
is 0).  If this result is negative, complement it and display a negative
sign.

_____________________________________________________________________