Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Decimal division

Status
Not open for further replies.

Mosaic

Well-Known Member
Hi :

Observe this calc:

1234/100 = 12.34

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?
 
The simple way is to just convert the 1234 and place the decimal point in the right position.

Mike.
 
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.

Susan
 
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).

Susan
 
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.

Mike.
 
Thanx Mike & Susan:

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.
 
Last edited:
Guys I need a bit more help.

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.
 
Well it might work but I don't fully understand the padding of a 16bit divisor. Can you explain that to me?

BTW i need a 16bit div by 8 bit, but need a 16 bit result. Not an 8 bit as in that link
 
Last edited:
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.

Susan
 
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.

thanks again Susan!
 
Last edited:
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.

Susan
 
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.

Saved a lot of cycles there.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top