A 16bit divide by 8 bit magnitude giving results in decimal is desired.
I can use the stock 16bit/8bit divisor code from the pic list to get the 8 bit integer quotient.
Then multiply the 8bit remainder by 10 to get another 16 bit number (Mult8X8 asm code).
Do another 16bit/8bit divisor .....to get the first decimal. Repeat for the 2nd etc.
Jolly, but, is there a more elegant way of doing this in fewer mcu cycles?
That may be so but the principle Pommie was showing you is still the same - scale the numerator values up, use integer division and then scale the values down again. It's also referred to as "fixed point" numbers where there is an implied decimal point at some fixed offset.
For example represent 1234 as 12340, 12340/100 = 123.4 (floating point) = 123 which represents 12.3. If you want more "decimal places" then you may need to go to 32 bits.
It all depends on the precision you are after. If you stick with 16 bits and are happy with 1 decimal point, then 26340/141=186. This can be "interpreted" as 18.6. Now the "real" answer is 18.68 so there is a bit of difference - does this matter? If it does then go to 32-bits and do the equivalent of 263400/141 = 1868. (In real numbers this would be 1868.09 - even there you only have a tiny error).
You would do 263400/141 to get 1868 and then place the decimal point to get 18.68.
I often use fixed point maths to carry out floating point type calculations. For example, say I wanted to do 123*PI, I would multiply PI by 256 to get 802, do the multiplication to get 98892 and finally divide by 256 to get 386. I use 256 as it is simply shifting right/left by 8 bits. In this example you would need a 24 bit register to carry it out and it is known as 16.8 maths. Sixteen bits for the integer and 8 bits for the fractional part.
I applied the technique....it reduced my coding by 38 bytes of asm and improved legibility of the code and accuracy of the results!.
By knowing the limits of the numbers I was dealing with I was able to do it ( to 2 decimal places) with the 16bit integer math.
I need to have a reasonably fast 16bit div by 8bit asm code fragment.
Can you recommend some asm to do this?
I looked at this:
but I am not sure I understand why the 8 bit divisor has to be padded out. I won't know if my divisor is 8 or 6 or 4 bit.....I know it is 8 bit or less.
There are 2 parts to the answer. The first is that you don't really need to know if you are using 4, 6 or 8 bit values in that you need to put the actual value into the divisor field so that the maximum value will have the top bit set. For example, if you have 8 bit divisors, then just use the top byte; if you have 6 bit divisors, then use the top 6 bits and pad the bottom 2 bits with zeros.
And that leads to the second part of the answer as to why you need to pad the divisor at all. It all has to do with the "subtract and shift" method used to perform the division. It is actually the same as the old-school "long division" technique. You make the divisor as large as possible (by padding the lower digits) and start subtracting from the dividend until the divisor is larger than the dividend. You record the number of times you have performed the subtraction. (This is the same as "guess the digit; multiply the divisor by that and subtract from the dividend" except that you don't need to "guess", you perform multiple subtractions until you can go no further). You then shift the divisor down by "digit" (decimal, binary etc) and repeat to get the next "digit" of the quotient. When you end up with the final quotient, you will need to scale this as necessary.
I suggest that you try it with a few "decimal" examples and you will soon see how it operates.
There are 2 parts to the answer. The first is that you don't really need to know if you are using 4, 6 or 8 bit values in that you need to put the actual value into the divisor field so that the maximum value will have the top bit set. For example, if you have 8 bit divisors, then just use the top byte; if you have 6 bit divisors, then use the top 6 bits and pad the bottom 2 bits with zeros.
Doesn't this mean I have to Know how many bits my divisor has to start with?
So I need to code a precursor routine to check the bits of the divisor and then stuff the divisor variables in the example?
Update:
I built the precursor code to bitpad and count bits in the divisor and configured the code to handle 8bit max divisors.
It works well...I get around 200 inst. per division.
here's the code that I used to do this:
Code:
clrf Tmp2
movwf Tmp1; save Divisor
movf Tmp1,f
skpnz
return; don't calc if divisor is zero
bcf STATUS,C; clr Carry
Padbits;
rlf Tmp1,f
incf Tmp2,f
skpc
goto Padbits
rrf Tmp1,f; restore carry bit to Tmp1 bit 7 (now a left justified divisor)
decf Tmp2,w; correct bitpad count
sublw .8; as we are using an single byte as the divisor
This leaves the # of bits in the divisor in wreg for use in the next step.
It means that you have to know the total size of your divisor, but if you don't know that then you really should not be at the coding stage yet.
If you have some places where your divisor is 4 bits wide (i.e. can be unsigned values from 0 to 15) and other places where it can be 8 bits wide (0 to 255) then you either need 2 routines (one for each) OR an entry point for the 4-bit values where you pad them on the left (MSBs) with 4 zeros and then trat it as an 8 bit value.
Of course, in all this you have to be careful about using small divisor values, because each "subtract and count" loop only removes the amount of the divisor - if it is small and the dividend is (relatively) large, then you end up with making LOTS of subtractions. Personally, I use a pre-scaling function that makes sure that the MSB of the divisor (and dividend) are both 1 and perform the whole division at the binary level (1-bit shifts) and then correct the quotient later. In this way I know I have at most 1 subtraction per iteration and as many iterations as there are bits in the quotient (I'm talking the number of instructions that are executed, not just the number of instructions in the code).
I've written some "infinite precision" routines using this technique that perform well.
Perhaps you can share a sample of that type of division for me to study?
I found a spot in my code that's predictable and replaced the universal division with this.
Code:
;Do a 12 bit divide by .16.
movlw b'11110000'
andwf TempL,f; clr low nybble.
swapf TempL,f; swap nybbles
swapf TempH,w;swap nybbles
andlw b'11110000'; clr the current lo nybble (was hi nybble b4 swap)
iorwf TempL,w ; unite nybbles to make = div by 16 result in wreg.