PIC Microcontoller Math Method

2 ^ X 16 Bit 0.16 notation

by Nikolai Golovchenko debugged by Daniel Schouten [daniels at XS4ALL.NL]

Well, let's calculate y=2^x! x is Q6.10 notation (always negative with implicit sign). Output y has Q0.16 notation (always less then 1). Therefore x should not be zero (2^0 = 1).

y is in the range from 0.9993 (x = -1/1024) to 5e-20 (x = -64 + 1/1024). Okay....

y=2^x

y=2^-(32*x.15+16*x.14+...1*x.10 + 0.5*x.9+0.25*x.8+...)
    \========================/   \===================/
          x_int                         x_frac

x_int = 0:1:63
x_frac = 0:1/1024:(1-1/1024)

y=2^-(x_int+x_frac)

y=2^(-x_int) * 2^(-x_frac)

y= (2^(-x_frac)) >> x_int
=========================

So we first find 2^-x_frac using a table and then shift the result right x_int times.

First we should convert the fraction part through a table to a new fraction part. 2^(-0..-1) = 0.5..1

; y = 2^x2
;Input: x2hi:x2lo
;Output: yhi:ylo

;Form table index from 4 higher bits of x2 fraction part
        rlf x2lo, f
        rlf x2ho, w
        movwf index
        rlf x2lo, w
        rlf index, f
;here middle 6 bits of x2lo contain multiplier for linear
;interpolation, and index has 4 lower bits of table pointer
        rlf index, w    ;multiply index by 2 for 16 bit
                        ;entries addressing
        andlw 0x1E      ;and clear lsb and higher bits
        movwf index
;Read first point to temphi:templo
        call exp2_table

;clear result and subtract the first point from it
        clrf yhi
        clrf ylo
        movf templo, w
        subwf ylo, f
        movf temphi, w
        skpc
         incfsz temphi, w
          subwf yhi, f
;Read next point to temphi:templo
        incf index, f
        incf index, f
        call exp2_table
;find difference with previous point to temphi:templo
        movf ylo, w
        addwf templo, f
        movf yhi, w
        skpnc
         incfsz yhi, w
          addwf temphi, f
;multiply the difference by the next 6 fraction bits in
;x2lo and divide by 64. Then subtract it from the current
;result in yhi:ylo
        movlw 6
        movwf index     ;use index as a loop counter
        rrf x2lo, f     ;align the 6 bits to the lsb

        clrf temp       ;we will use another temp
                        ;for mul purposes

        rrf yhi, f      ;rotate y right 2 times
        rrf ylo, f      ;to make easy multiplication.
        rrf temp, f     ;lower bits will go to temp
        rrf yhi, f
        rrf ylo, f
        rrf temp, f
exp2loop
        rrf x2lo, f      ;get next multiplier bit
        bnc exp2loopnext ;skip subtraction if carry is clear

        movf templo, w
        subwf temp, f
        movf temphi, w
        skpc
         incfsz temphi, w
          subwf ylo, f
        skpc
         decf yhi, f

exp2loopnext

        rrf yhi, f
        rrf ylo, f
        rrf temp, f
        decfsz index, f
         goto exp2loop
;shift the result 8 bits left
        movf ylo, w
        movwf yhi
        movf temp, w
        movwf ylo

;Now we will shift y right the number of times
;equal to the integer part of x2
        clrc            ;align integer bits
        rrf x2hi, f     ;to lsb
        clrc
        rrf x2hi, f

        test x2hi
        skpnz
         return         ;done
exp2loop2
        clrc
        rrf y2hi, f
        rrf y2lo, f
        decfsz x2hi, f
         goto exp2loop2
        return          ;done

Now we need to calculate the table...
1-2.^(0:-1/16:-1)
ans =

  Columns 1 through 4 
                  0   0.04239671930143   0.08299595679533   0.12187391981335
  Columns 5 through 8 
   0.15910358474629   0.19475483402537   0.22889458729603   0.26158692703025
  Columns 9 through 12 
   0.29289321881345   0.32287222653155   0.35158022267450   0.37907109396326
  Columns 13 through 16 
   0.40539644249864   0.43060568262165   0.45474613366737   0.47786310878629
  Column 17 
   0.50000000000000

Multiplied by 65536 and rounded
round(65536*(1-2.^(0:-1/16:-1)))
ans =

  Columns 1 through 6 
           0        2779        5439        7987       10427       12763
  Columns 7 through 12 
       15001       17143       19195       21160       23041       24843
  Columns 13 through 17 
       26568       28220       29802       31317       32768
» 

So, the table would be:

exp2_table
        movf index, w
        call exp2tableStart
        movwf templo
        incf index, w
        call exp2tableStart
        movwf temphi
        return

exp2tableStart
        addwf PCL, f
        DT     0 & 0xFF,     0 >> 8,  2779 & 0xFF,  2779 >> 8
        DT  5439 & 0xFF,  5439 >> 8,  7987 & 0xFF,  7987 >> 8
        DT 10427 & 0xFF, 10427 >> 8, 12763 & 0xFF, 12763 >> 8
        DT 15001 & 0xFF, 15001 >> 8, 17143 & 0xFF, 17143 >> 8
        DT 19195 & 0xFF, 19195 >> 8, 21160 & 0xFF, 21160 >> 8
        DT 23041 & 0xFF, 23041 >> 8, 24843 & 0xFF, 24843 >> 8
        DT 26568 & 0xFF, 26568 >> 8, 28220 & 0xFF, 28220 >> 8
        DT 29802 & 0xFF, 29802 >> 8, 31317 & 0xFF, 31317 >> 8
        DT 32768 & 0xFF, 32768 >> 8
 IF (($ - 1) >> 8) - ((exp2tableStart + 1) >> 8) != 0
        error 'exp2 table crossed 8-bit boundary'
 ENDIF

And the cleaned up version is below... Yeah, this one was harder and not really the reverse of log!

Don't forget to check x and y for zero in x^y.

#include <p16f84.inc>

	cblock 0x20
	xhi, xlo
	templo, temphi, temp
	index
	x2hi, x2lo
	endc


#define tval 65300

	movlw tval & 0xFF
	movwf xlo
	movlw tval >> 8
	movwf xhi
	call log2
	nop
	call exp2
	nop
	

;************************************************
; EXP2 routine for fixed point unsigned
; 16 bit values in 6.10 notation (implicit minus)
;
; Input: x2hi:x2lo unsigned Q6.10 (implicit minus)
; Output: xhi:xlo unsigned Q0.16
; Temporary: index, templo, temphi, temp
;
; Maximum error: 0.025% (for x2 < 1)
;
; Size:  121 words
; Time: min 2+8+18*2+10+16+10*6-1+6+2=139
;       max 2+8+18*2+10+16+17*6-1+6+1+14+6*16-1+2=291
;
; Note: Zero input is illegal! Will result in
;	zero output. (2^0 = 1, but x is always less
;	than 1)
;
; 30 Sep 2000 by Nikolai Golovchenko
; 02 Oct 2000 debugged by Daniel Schouten [daniels at XS4ALL.NL]
;************************************************
exp2
;Form table index from 4 higher bits of x2 fraction part
        rlf x2lo, f
        rlf x2hi, w
        movwf index
        rlf x2lo, w
        rlf index, f
;here middle 6 bits of x2lo contain multiplier for linear
;interpolation, and index has 4 lower bits of table pointer
        rlf index, w    ;multiply index by 2 for 16 bit
                        ;entries addressing
        andlw 0x1E      ;and clear lsb and higher bits
        movwf index
;Read first point to temphi:templo
        call exp2_table

;clear result and subtract the first point from it
        clrf xhi
        clrf xlo
        movf templo, w
        subwf xlo, f
        movf temphi, w
        skpc
         incfsz temphi, w
          subwf xhi, f
;Read next point to temphi:templo
        incf index, f
        incf index, f
        call exp2_table
;find difference with previous point to temphi:templo
        movf xlo, w
        addwf templo, f
        movf xhi, w
        skpnc
         incfsz xhi, w
          addwf temphi, f
;multiply the difference by the next 6 fraction bits in
;x2lo and divide by 64. Then subtract it from the current
;result in xhi:xlo
        movlw 6
        movwf index     ;use index as a loop counter
        rrf x2lo, f     ;align the 6 bits to the lsb

        clrf temp       ;we will use another temp
                        ;for mul purposes

        rrf xhi, f      ;rotate x right 2 times
        rrf xlo, f      ;to make easy multiplication.
        rrf temp, f     ;lower bits will go to temp
        rrf xhi, f
        rrf xlo, f
        rrf temp, f
exp2loop
        rrf x2lo, f      ;get next multiplier bit
        bnc exp2loopnext ;skip subtraction if carry is clear

        movf templo, w
        subwf temp, f
        movf temphi, w
        skpc
         incfsz temphi, w
          subwf xlo, f
        skpc
         decf xhi, f

exp2loopnext
        rrf xhi, f
        rrf xlo, f
        rrf temp, f
        decfsz index, f
         goto exp2loop
;shift the result 8 bits left
        movf xlo, w
        movwf xhi
        movf temp, w
        movwf xlo

;Now we will shift x right the number of times
;equal to the integer part of x2
        clrc            ;align integer bits
        rrf x2hi, f     ;to lsb
        clrc
        rrf x2hi, f

        tstf x2hi
        skpnz
         return         ;done

        movlw 17    	;limit shifts number to 16 maximum
        subwf x2hi, w
        movlw 16
        skpnc
         movwf x2hi

;if fractional part is zero than set carry, else
;clear carry
	movf xlo, w
	iorwf xhi, w
	setc
	skpz
	 clrc
;limit number of si
;perform shifts
exp2loop2
        rrf xhi, f
        rrf xlo, f
        clrc
        decfsz x2hi, f
         goto exp2loop2
        return          ;done

exp2_table
        movf index, w
        call exp2tableStart
        movwf templo
        incf index, w
        call exp2tableStart
        movwf temphi
        return

exp2tableStart
        addwf PCL, f
        DT     0 & 0xFF,     0 >> 8,  2779 & 0xFF,  2779 >> 8
        DT  5439 & 0xFF,  5439 >> 8,  7987 & 0xFF,  7987 >> 8
        DT 10427 & 0xFF, 10427 >> 8, 12763 & 0xFF, 12763 >> 8
        DT 15001 & 0xFF, 15001 >> 8, 17143 & 0xFF, 17143 >> 8
        DT 19195 & 0xFF, 19195 >> 8, 21160 & 0xFF, 21160 >> 8
        DT 23041 & 0xFF, 23041 >> 8, 24843 & 0xFF, 24843 >> 8
        DT 26568 & 0xFF, 26568 >> 8, 28220 & 0xFF, 28220 >> 8
        DT 29802 & 0xFF, 29802 >> 8, 31317 & 0xFF, 31317 >> 8
        DT 32768 & 0xFF, 32768 >> 8
 IF (($ - 1) >> 8) - ((exp2tableStart + 1) >> 8) != 0
        error 'exp2 table crossed 8-bit boundary'
 ENDIF
;************************************************