CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

divide by 10

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
planet69



Joined: 25 Aug 2010
Posts: 8

View user's profile Send private message

divide by 10
PostPosted: Tue Sep 07, 2010 8:17 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Sep 07, 2010 11:51 pm     Reply with quote

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:
Quote:

quo = 24 rem = 3

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 &quotient, 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

View user's profile Send private message

PostPosted: Wed Sep 08, 2010 2:16 am     Reply with quote

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

Best Wishes
planet69



Joined: 25 Aug 2010
Posts: 8

View user's profile Send private message

PostPosted: Wed Sep 08, 2010 3:50 am     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Sep 08, 2010 4:57 am     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Sep 08, 2010 5:47 am     Reply with quote

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

View user's profile Send private message Visit poster's website

Re: divide by 10
PostPosted: Wed Sep 08, 2010 8:01 am     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Sep 08, 2010 8:23 am     Reply with quote

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

View user's profile Send private message

Re: divide by 10
PostPosted: Wed Sep 08, 2010 12:05 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Sep 08, 2010 12:20 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Sep 08, 2010 7:34 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Thu Sep 09, 2010 3:03 am     Reply with quote

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
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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