Big number arithmetic problem

cpedw

Senior Member
I want to calculate a PWM period from 163,200/x where x is a word variable in the range 640 to about 2000. I could do it thus: 40800/x*4 (163200/4=40800) but then the result loses resolution and changes in steps of 4.

Is there a way to rearrange the arithmetic to achieve resolution in steps of 1?
 

AllyCat

Senior Member
Hi,

Personally, I always just drop in a version of a code snippet subroutine that I devised when first using PICaxe. There might be other snippets or methods, for example I saw that hippy recently did one for binary division from first principles.

Alternatively, a "trick" that I've used (with CALIBADC values) to give double the resolution, is to split the numerator into two halves, add half of the divisor to one of them, divide both and then add the results. That might be extended by dividing into 3 (or 4), but I'd just stick to what I know works. ;)

Cheers, Alan.
 

marks

Senior Member
Hi cpedw,
just scale like ur'v done or using a high word
Rich (BB code):
w1 = 2000 'test 163200 / 640 to 2000
SerTxd(cr,lf,#w1,"  ")
w1=w1 /4 : w1= 40800 / w1
SerTxd(#w1)
Rich (BB code):
w1 = 640 'test 163200 / 640 to 2000
SerTxd(cr,lf,#w1,"  ")
w1=w1 **26317 : w1= $FFFF / w1
SerTxd(#w1)
 
Last edited:

cpedw

Senior Member
Thanks for all the suggestions. I have tested marks' suggestion and it works in the simulator. It's also short and doesn't need any extra variables. I just wish I understood how it works.

Where does 26317 come from?
 

Flenser

Senior Member
Marks,
I agree with cpedw, your second code is very neat.
I would also be interested in a description of how to calculate the constant for other divisions?
 

AllyCat

Senior Member
Hi,

Adding 370 bytes of subroutines before you start does seem rather like overkill ! The ** 26317 optimally scales the 163200 to fit in a 16-bit word (65535), but the calculation is still only to 16-bit accuracy so there are some errors (for example note the run of 234 in the range 690 - 700). Splitting the calculation into 3 seems to work well, but uses as many bytes as the 31-bit division routine! The binary division is very slow but, as the result is only 8 bits, the speed can be doubled, by reducing to only 8 iterations.

All these methods give a truncated (smaller) result (except for the direct /4 because its divisor may get truncated), but a "rounded" (up) result (i.e. when the remainder > half the divisor) can be obtained by adding half the divisor to the numerator. That's easy with the binary division method (then just ignore the remainder) but rather "messy" with all the other methods which prescale the numerator.

Here's my /3 method and sample results for all the methods:
Code:
symbol DIV3 = 163200 / 3     ; = 54400
for w0 = 690 to 700
  w3 = w0 / 3
  w1 = DIV3 + w3 + w3 / w0
  w1 = DIV3 + w3 / w0 + w1
  w1 = DIV3 / w0 + w1
  sertxd(cr,lf,#w0,"  ",#w1)
next
#rem
div  /4  opt16  /3  full binary
690  237  236  236  236:rem 360
691  237  236  236  236:rem 124
692  235  236  235  235:rem 580
693  235  235  235  235:rem 345
694  235  235  235  235:rem 110
695  235  234  234  234:rem 570
696  234  234  234  234:rem 336
697  234  234  234  234:rem 102
698  234  234  233  233:rem 566
699  234  234  233  233:rem 333
700  233  233  233  233:rem 100
Cheers, Alan
 
Last edited:

Flenser

Senior Member
cpedw,

By including the remainder in the calculation you can calculate the answer with a resolution of 1.
Also, because the numerator 40800 is exactly 1/4 of 163,200 the results after including the remainder are exactly correct.

This code calculates the integer division. i.e. it truncates the result.
Code:
FOR w1 = 640 to 2000
w2=40800//w1*4 /w1
w2=40800/w1*4 + w2
SerTxd(#w1, " ", #w2, cr, lf)
next w1
This code calculates a rounded result:
Code:
FOR w1 = 640 to 2000
w2=w1/2
w2=40800//w1*4 + w2/w1
w2=40800/w1*4 + w2
SerTxd(#w1, " ", #w2, cr, lf)
next w1
 

marks

Senior Member
I do like the way your've rounded things up there! simple half step improvement.
Multiplying by ten we can also extract two decimals,,for testing if we have time and the patience.
Rich (BB code):
for W0= 640 to 700 '163200 / 640 to 2000

SerTxd(cr,lf,#W0,"  ") '690
  w1 = 16320 /w0 *10
w2 = 16320 //w0 *10
w1 = w2 /w0 +w1 : SerTxd(#w1,".")
w1 = w2//w0 *10 /w0 : SerTxd(#w1)
w1 = w2//w0 *10 //w0 *10/w0 : SerTxd(#w1," ") '236.52

w1 = w0 **26316 : w1 = 65531 / w1 '65531 /163200 x65536 +1 = 26316
 SerTxd(#w1,"  ") '236

W2 = w0/2 ' round up
w1 = 16320 //w0 *10 +W2 /w0
w1 = 16320 /w0 *10 +w1 
SerTxd(#w1,"  ") '237

next W0
 
Top