PICAXE Basic only supports arithmetic up to 16 bits, which can make some calculations difficult. Often it's possible to keep the calculation within bounds by alternating multiplication and division, etc., but sometimes accuracy suffers. For example, suppose a READADC10 value needs to be scaled or "calibrated" by +1%. That requires multiplication by 101 and division by 100, but multiplication first could create an overflow (10 bits * 7 bits = 17 bits) and division used first loses accuracy.
However, PICAXE Basic does offer "partial" multiplication up to a "Double-Word" (32 bits) using the * and ** operators, so the aim of this subroutine is to provide a "reverse path", i.e. accepting a Double-Word input (and a Divisor) and returning Single-Words for Result and Remainder. Note that this is NOT intended as a full "16-bit maths" routine, more a "Get out of Jail Free card" for "difficult" calculations. Several 32 bit routines have been discussed in this thread, including a full 32 by 32 bit division from hippy, and I hope to document an "improved" version of my full 32 bits by 16 bits version soon.
To keep the code simple and compact (it actually uses less memory than a BINTOASCII word command) a couple of restrictions are necessary. Firstly, the divisor is limited to 15 bits (which automatically implies that the "Remainder" will also fit into a single word). The reason is that PICAXE Basic doesn't support a "Carry/Borrow" flag, so (to keep the code simple) the top bit of the numerator is used for that flag during subtraction, leaving only 15 bits for the divisor (i.e. < 32,768). The other restriction is that the "Result" is also limited to a single (16-bit) Word (i.e. < 65,536). Thus the numerator cannot be more than 31 bits (just over 2 billion) and is further restricted with smaller divisors. For example if dividing by ten, then the maximum numerator is 655,359 (20 bits).
Because of these restrictions, a short (optional) "preamble" code tests for data validity and immediately returns an "Error Code" when appropriate. A detected input error leaves the data unchanged and avoids wasting time calculating an incorrect result. However, since PICAXE Basic doesn't implement any run-time error-handling facilities, the data validation section is mainly for demo/testing and can be omitted, with (as usual) the responsibility for the calculations staying within bounds being that of the software writer.
So here is the code which uses about 37 bytes for the simple (non-validating) subroutine and 7 bytes of RAM, i.e. four bytes for the double-word numerator, two bytes for the divisor word and a single byte for loop counting and the return of the Error Code when appropriate:
On Return, the "Remainder" replaces the "High Numerator" word and the "Result" replaces the "Low Numerator" word, but the Divisor is unchanged (i.e. the code behaves rather like a w1 = w1 / w2 assignment). So if it is desired to retain the value of the numerator, then the original word values must be copied to the numhi and numlo variables before entering the subroutine. Of course, the calculation takes considerably longer than the embedded PICAXE division, about 100ms with a 4MHz clock. However, this time is moderately constant and could be made even more consistent with a minor addition to the code, if desired. Thus the routine might "double up" as a PAUSE 100 in some instances.
Cheers, Alan.
However, PICAXE Basic does offer "partial" multiplication up to a "Double-Word" (32 bits) using the * and ** operators, so the aim of this subroutine is to provide a "reverse path", i.e. accepting a Double-Word input (and a Divisor) and returning Single-Words for Result and Remainder. Note that this is NOT intended as a full "16-bit maths" routine, more a "Get out of Jail Free card" for "difficult" calculations. Several 32 bit routines have been discussed in this thread, including a full 32 by 32 bit division from hippy, and I hope to document an "improved" version of my full 32 bits by 16 bits version soon.
To keep the code simple and compact (it actually uses less memory than a BINTOASCII word command) a couple of restrictions are necessary. Firstly, the divisor is limited to 15 bits (which automatically implies that the "Remainder" will also fit into a single word). The reason is that PICAXE Basic doesn't support a "Carry/Borrow" flag, so (to keep the code simple) the top bit of the numerator is used for that flag during subtraction, leaving only 15 bits for the divisor (i.e. < 32,768). The other restriction is that the "Result" is also limited to a single (16-bit) Word (i.e. < 65,536). Thus the numerator cannot be more than 31 bits (just over 2 billion) and is further restricted with smaller divisors. For example if dividing by ten, then the maximum numerator is 655,359 (20 bits).
Because of these restrictions, a short (optional) "preamble" code tests for data validity and immediately returns an "Error Code" when appropriate. A detected input error leaves the data unchanged and avoids wasting time calculating an incorrect result. However, since PICAXE Basic doesn't implement any run-time error-handling facilities, the data validation section is mainly for demo/testing and can be omitted, with (as usual) the responsibility for the calculations staying within bounds being that of the software writer.
So here is the code which uses about 37 bytes for the simple (non-validating) subroutine and 7 bytes of RAM, i.e. four bytes for the double-word numerator, two bytes for the divisor word and a single byte for loop counting and the return of the Error Code when appropriate:
Code:
#picaxe 20m2 ; And most others
; Divide Double-Word (w3:w2) by w1 using binary Long Division
; w2 = Result; w3 = Remainder; w1 unchanged; b0 = 0 if input error
; Max divisor 32767 (15 bits), max result 65535 (16 bits)
; AllyCat 15th June 2012
symbol NUMERATOR = 123456789 ; Test Data
symbol DIVISOR = 10000
symbol NUMERHI = NUMERATOR / $10000
symbol NUMERLO = NUMERATOR & $0FFFF
symbol topbit = $8000 ; Value of "carry" bit (when shifting left)
symbol numhi = w3 ; Numerator Hi, Returns Remainder
symbol numlo = w2 ; Numerator Lo, Retuns Result
symbol divis = w1 ; Divisor (max 15 bits), Unchanged on Return
symbol pass = b0 ; Loop counter, on return = 0 if data error
Testdiv31: ; Test routine
numhi = NUMERHI ; Numerator High -> Remainder, max 15 bits (32767)
numlo = NUMERLO ; Numerator Low -> Result, max 16 bits (65535)
divis = DIVISOR ; Divisor, max 15 bits (32767)
gosub div31v ; Validate input data and divide numerator by divisor
if pass = 0 then
sertxd ("Bad Divisor",cr,lf)
do : loop
endif
sertxd ("Result=",#numlo," Remainder= ",#numhi,cr,lf) ; Print result
goto Testdiv31
div31v: ; Validate data and Divide numerator by divisor
pass = 0 ; Pre-set the error marker
if divis => topbit OR numhi >= divis then done ; Data Error so quit
div31: ; Enter here to Divide with NO error check
for pass = 0 to 15 ; Repeat for each bit position
numhi = numhi + numhi ; Start to shift numerator left (top bit is lost)
if numlo >= topbit then ; Carry bit from low word is '1'
numhi = numhi + 1 ; Add the carry to hi word
endif
numlo = numlo + numlo ; End of Shift
if numhi < divis then nosub ; Jump if can't subtract
numhi = numhi - divis ; Subtract divisor and ...
numlo = numlo + 1 ; Add the flag to result (in low word)
nosub:
next pass
done:
return
Cheers, Alan.