|
|
View previous topic :: View next topic |
Author |
Message |
planet69
Joined: 25 Aug 2010 Posts: 8
|
divide by 10 |
Posted: Tue Sep 07, 2010 8:17 pm |
|
|
Please show me a simpler way of implementing:
Code: | int8 quotient;
int8 remainder;
int8 numerator;
quotient = numerator / 10;
remainder = numerator % 10; |
The two line generates an awful lot of assembly lines. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Sep 07, 2010 11:51 pm |
|
|
If you want to trade code size for execution time, there is always the
repetitive subtraction method as shown below.
The routine below works for positive numbers from 0 to 255. It returns
the quotient and remainder in two reference parameters. The routine is
very short in terms of the generated ASM code. Here's the result:
Code: |
#include <18F452.h>
#fuses XT,NOWDT,PUT,BROWNOUT,NOLVP
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
void div10(int8 numerator, int8 "ient, int8 &remainder)
{
quotient = 0;
while(numerator >= 10)
{
numerator -= 10;
quotient++;
}
remainder = numerator;
}
//======================================
void main(void)
{
int8 quotient;
int8 remainder;
int8 numerator;
numerator = 243;
div10(numerator, quotient, remainder);
printf("quo = %u rem = %u \r", quotient, remainder);
while(1);
}
|
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19541
|
|
Posted: Wed Sep 08, 2010 2:16 am |
|
|
It is worth also putting this in perspective. Division in particular, involves a lot of work. Try writing your own assembler code for this, as an exercise in understanding just how much the processor has to do... :(
Now, The original example, generates about the 'worst case', since it performs a generic division, twice. The modulus operator, divides the number by ten, to get the remainder, and the division does the same. A lot of code, with half of it unnecessary. PCM shows how to produce the 'best case' code size version, but taking quite a lot more processor time as the 'downside'. There is however a 'half way' solution, which saves a lot of space, and is quicker. The key is of course that it'd be much easier if you could just perform the division once, and get both the quotient and remainder, from this. In the past, people have spent time to work out how the compiler holds values to allow this to be done, but after some requests, CCS added this ability with the 'div' operator.
So:
Code: |
#include <STDLIB.H>
div_t divval;
int8 numerator;
divval=div(numerator,10);
|
Then divval.quot, and divval.rem, will be the quotient, and remainder, and the maths will use half the space, of the original, and half the time...
Best Wishes |
|
|
planet69
Joined: 25 Aug 2010 Posts: 8
|
|
Posted: Wed Sep 08, 2010 3:50 am |
|
|
thanks PCM p. & Ttelmah
using mplab sim, i'm putting the codes into test.
i find that:
Code: | quotient = numerator / 10;
remainder = numerator % 10; |
chows down approx. 195 instruction cycles and not to mention large code size.
PCM p's example:
Code: | while(numerator >= 10)
{
numerator -= 10;
quotient++;
}
remainder = numerator;
} |
works best if numerator is small
Ttelmah's
Code: | divval=div(numerator,10); |
runs about 170 instruction cycles and the numerator must be a signed variable.
well, adapting PCM p.'s example:
Code: | quotient = numerator / 10;
remainder = numerator - (quotient * 10); |
this actually halve the execution time but not the space though.
found an example from piclist
Code: | //Y = X/10 same as: Y = (X + X/2 + X/8 - X/64) / 16
quotient = (numerator + (numerator/2) + (numerator/8) - (numerator/64)) / 16;
remainder = numerator - (quotient * 10); |
this is much faster and space saving provided that the numerator is 159 and below
Last edited by planet69 on Thu Sep 09, 2010 3:39 am; edited 1 time in total |
|
|
John P
Joined: 17 Sep 2003 Posts: 331
|
|
Posted: Wed Sep 08, 2010 4:57 am |
|
|
Wouldn't
Code: |
remainder = numerator - (quotient * 10);
|
work faster as
Code: |
remainder = numerator - (quotient * 8 + quotient * 2);
|
? |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Wed Sep 08, 2010 5:47 am |
|
|
John P wrote: | Wouldn't
Code: |
remainder = numerator - (quotient * 10);
|
work faster as
Code: |
remainder = numerator - (quotient * 8 + quotient * 2);
|
? | The result is processor dependent. On a PIC16 the shift could be faster but a PIC18 has an 8-bit hardware multiplier unit, there it might even be slower (not tested). |
|
|
andrewg
Joined: 17 Aug 2005 Posts: 316 Location: Perth, Western Australia
|
Re: divide by 10 |
Posted: Wed Sep 08, 2010 8:01 am |
|
|
planet69 wrote: | Please show me a simpler way of implementing:
Code: | int8 quotient;
int8 remainder;
int8 numerator;
quotient = numerator / 10;
remainder = numerator % 10; |
The two line generates an awful lot of assembly lines. |
This annoys me greatly too, and I haven't found a solution. It's even more annoying when you analyse the generated code and find that they're identical, apart from where the two values are retrieved - i.e. CCS is using a generic divide function that calculates quotient and remainder simultaneously, but only lets you grab one result per call.
If only they had implemented div() and ldiv() to take advantage of this internal data, but no. _________________ Andrew |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19541
|
|
Posted: Wed Sep 08, 2010 8:23 am |
|
|
Yes, I was forgetting that div, only supports signed values.
The annoying thing here, is that the existing division code, certainly does generate the remainder, and it'd be a matter of a few lines only for CCS to provide a division that returned both quotient and remainder...
Historically (a long time ago now), when dealing with larger values (int32), I found that on the compilers at the time, the remainder was left in the scratch area, and you could 'cheat', by calling a function that is declared to return a value, which then performed the division, and didn't return anything. If you then read the returned value, you got the remainder. This though appears not to work with the current compilers. :(
However on a quick test, the return value, does seem to be sitting in the lowest byte of the scratch area, and if you set a byte variable to access this, can be read.
Problem of course, is that this is 'not warranted', and likely to change on future compilers. However on a basic PIC18 test:
Code: |
#include "C:\Program Files\PICC\USBtest\testdiv.h"
#byte rem=0
void main()
{
int8 quot, num, den;
den=3;
num=35;
quot=num/den;
while(TRUE);
}
|
The code ends with quot and rem containing the required values!....
Best Wishes |
|
|
planet69
Joined: 25 Aug 2010 Posts: 8
|
Re: divide by 10 |
Posted: Wed Sep 08, 2010 12:05 pm |
|
|
andrewg wrote: |
This annoys me greatly too, and I haven't found a solution. It's even more annoying when you analyse the generated code and find that they're identical, apart from where the two values are retrieved - i.e. CCS is using a generic divide function that calculates quotient and remainder simultaneously, but only lets you grab one result per call.
If only they had implemented div() and ldiv() to take advantage of this internal data, but no. |
pretty sure it does...
check out Ttelmah's example
Code: | #include <STDLIB.H>
div_t divval;
int8 numerator;
divval=div(numerator,10);
//divval.quot, and divval.rem, will be the quotient, and remainder |
anyway, i'm running the tests on a PIC18 |
|
|
John P
Joined: 17 Sep 2003 Posts: 331
|
|
Posted: Wed Sep 08, 2010 12:20 pm |
|
|
Another approach could be
Code: |
int8 numerator;
int8 quotient;
quotient = (int8)(((int16)numerator * 205) / 2048);
|
The calculation 205/2048 gives a result that's very close to division by 10, and only involves a division by power of 2. But whether it's worth all the foolery, I don't know.
If you want to do the multiplication by powers of 2 also, then 205 = 128 + 64 + 8 + 4 + 1. |
|
|
planet69
Joined: 25 Aug 2010 Posts: 8
|
|
Posted: Wed Sep 08, 2010 7:34 pm |
|
|
John P wrote: | Wouldn't
Code: |
remainder = numerator - (quotient * 10);
|
work faster as
Code: |
remainder = numerator - (quotient * 8 + quotient * 2);
|
? |
actually (quotient * 10) is few instruction cycles faster than running on a breakdown of it. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19541
|
|
Posted: Thu Sep 09, 2010 3:03 am |
|
|
planet69 wrote: | John P wrote: | Wouldn't
Code: |
remainder = numerator - (quotient * 10);
|
work faster as
Code: |
remainder = numerator - (quotient * 8 + quotient * 2);
|
? |
actually (quotient * 10) is few instruction cycles faster than running on a breakdown of it. |
Different on a PIC16 though.
Best Wishes |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|