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

hex to decimal converter - efficiency

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



Joined: 20 Nov 2013
Posts: 5

View user's profile Send private message

hex to decimal converter - efficiency
PostPosted: Wed Dec 11, 2013 10:02 am     Reply with quote

Hi everyone,

I am doing a [sort of] hex to decimal converter, and it works, but I feel that I am doing it "the hard way"

I searched the forums and found a few posts but they deal with ASCII hex, this isn't quite that.

I have my code commented to what is going on.



Code:
   
   // dataBytes = 4 hex digits that are inputs from the user for data values. 
   // These values need to be converted from hex to decimal.
   // The greatest any hex number will be is 0x9.
   
   uint8 byte_0;
   uint8 byte_1;                        // if dataByte = 0x1234 [ 1 2 3 4]
   uint8 tmp_byte = dataByte;               // gets the LSB of dataByte [x x 3 4], tmp_byte = 34 (hex)
   byte_1 = ((tmp_byte >>4) *10);             // shifts over the 3, then * 10, byte_1 = 30 (decimal)
   tmp_byte = tmp_byte << 4;               // shifts the 4 into MSB
   tmp_byte = tmp_byte >> 4;               // shifts the 4 back to LSB to remove the 3, and give tmp_byte a value of 4 (decimal)
   byte_1 += (tmp_byte);                  // adds 4 (decimal) to 30 decimal
   
   byte_0 = (dataByte >> 8);               // gets the MSB of dataByte [1 2 x x] byte_0 = 12 (hex)
   tmp_byte = byte_0;
   tmp_byte = tmp_byte <<4;
   tmp_byte = tmp_byte >>4;
   byte_0 = ((byte_0 >> 4) *10 );            // now byte_0 is 10 (1 * 10)
   byte_0 += tmp_byte;

   uint16 total_bytes = byte_1;
   total_bytes *=  100;
   total_bytes += byte_0;


This works, but I am using a 16f pic, with limited memory, currently my ROM is at 90%, but commonly I am getting issues to push it over 100%. If there are any tricks to clean this up I would greatly appreciate any direction.

Thank you,
io_joe
Gabriel



Joined: 03 Aug 2009
Posts: 1067
Location: Panama

View user's profile Send private message

PostPosted: Wed Dec 11, 2013 2:20 pm     Reply with quote

check out Printf()
I'm almost certain you can print to a variable... or at least I've always thought you could.

Also, you can consider using BCD which uses 4 bits per DEC digit... so you could store up to DEC 99 in a singe HEX byte.


G.
_________________
CCS PCM 5.078 & CCS PCH 5.093
Gabriel



Joined: 03 Aug 2009
Posts: 1067
Location: Panama

View user's profile Send private message

PostPosted: Wed Dec 11, 2013 2:36 pm     Reply with quote

i just re read your post...

are you just trying to get ASCII _NUMBER_ inputs like from a serial com to register as decimal?

... just substract 0x30.

G.
_________________
CCS PCM 5.078 & CCS PCH 5.093
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Wed Dec 11, 2013 4:20 pm     Reply with quote

Quote:

// dataBytes = 4 hex digits that are inputs from the user for data values.
// These values need to be converted from hex to decimal.
// The greatest any hex number will be is 0x9.

uint8 byte_0;
uint8 byte_1; // if dataByte = 0x1234 [ 1 2 3 4]

uint8 tmp_byte = dataByte; // gets the LSB of dataByte [x x 3 4], tmp_byte = 34 (hex)

You're actually describing BCD (binary coded decimal) format.
You have an int16 with four BCD digits, that can have a value from
0000 to 9999 (in BCD). You want to convert this to an unsigned int16
integer. This is the actual statement of your problem or request.

Do a Google search for this:
Quote:
fast bcd to binary conversion
Ttelmah



Joined: 11 Mar 2010
Posts: 19539

View user's profile Send private message

PostPosted: Thu Dec 12, 2013 2:21 am     Reply with quote

There are also several little optimisations that are 'PIC specific'. For instance the swap instruction (a PIC instruction that CCS gives you a C command to access), which in one instruction, swaps the high and low nibbles of a byte.
Then multiplying by ten, is more efficiently done by adding val*2+val*8. There was a thread about doing this as efficiently as possible here only a couple of weeks ago.

Key is that if you store intermediate results, it is much more efficient than recalculating them.

Something like:
Code:

//Your processor header here

#inline
int8 BCDtobin(int8 val)
{
   //take a single byte coded as two BCD digits and return the binary value
   int8 temp;
   temp=val;
   swap(temp);
   temp&=0xF;  //high nibble
   temp*=2;
   temp=temp+(temp*4); //efficient *10 *8+*2
   temp+=(val&0xF); //add the low nibble
   return temp;
}

int16 BCDtoLongbin(int16 val)
{
   //Now convert a 16bit BCD value to 16bit result;
   int16 ltemp, ltemp2;
   ltemp=BCDtobin(make8(val,0)); //convert the MSB
   //now need *100.
   ltemp*=4;
   ltemp2=ltemp*8;
   ltemp=ltemp+ltemp2+(ltemp2*2); //efficient *100 -> *64+*32+*4
   ltemp+=BCDtobin(make8(val,1)); //now the LSB
   return ltemp;
}

void main()
{
   int16 testval;
   
   testval=0x1234;
   testval=BCDtoLongbin(testval);
   //here 'testval' is 0xD54 = 3412 as the original
   while(TRUE) ;   
}

Now, this takes just 96 machine instructions to do the conversion, while the original version takes 182. Nearly twice as fast.
Removing the inline, costs about ten instruction times, but saves about 13 instructions on size.

I note that the original code is reversing the byte order. Treating the number as if it is stored MSB first in memory. I have coded this to be the same. If this is wrong, then just swap the 0's and 1's in the make8 operations.

May well be possible to improve further, haven't looked deeply.

As a comment have just compared the size using a PIC16, and it is 48 bytes smaller (using the non #inline form)

Best Wishes
io_Joe



Joined: 20 Nov 2013
Posts: 5

View user's profile Send private message

PostPosted: Thu Dec 12, 2013 10:10 am     Reply with quote

Thanks a ton for the info guys.

Ttelmah - wow! nice catch on the data coming in backwards on total_bytes, thank you!
dorinm



Joined: 07 Jan 2006
Posts: 38

View user's profile Send private message

PostPosted: Thu Dec 12, 2013 10:53 am     Reply with quote

interesting enough, i found an idea (intended for verilog) for binary to BCD
http://www.eng.utah.edu/~nmcdonal/Tutorials/BCDTutorial/BCDConversion.html , there might be done for the BCD to binary, for sure ;) ... just an idea ;)
temtronic



Joined: 01 Jul 2010
Posts: 9243
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Thu Dec 12, 2013 11:52 am     Reply with quote

hmm... another way... haven't done the code but...
if you breakdown the 2 bytes into nibbles...
ie: 0x1234 -> 1,2,3,4
then
sum=(4096*1)+(256*2)+(16*3)+(1x4)= 4660

don't know if it's faster, trying to thaw my -11*C toes from being outside....

be interesting to see if it is though.
jay
dorinm



Joined: 07 Jan 2006
Posts: 38

View user's profile Send private message

PostPosted: Thu Dec 12, 2013 3:18 pm     Reply with quote

more interesting, too! Smile

Code:

....................    //actually, we start here
....................    h=l;
002FE:  MOVFF  2C,2A
00302:  MOVFF  2B,29
....................     
....................    int16 k;
....................    k=(4096*(h&0xf000))+(256*(h&0x0f00))+(16*(h&0x00f0))+(h&0x000f);
00306:  ANDLW  00
00308:  MOVWF  00
0030A:  MOVF   2A,W
0030C:  ANDLW  F0
0030E:  MOVWF  03
00310:  MOVFF  00,02
00314:  SWAPF  00,W
00316:  MOVWF  30
00318:  CLRF   2F
0031A:  MOVLW  F0
0031C:  ANDWF  30,F
0031E:  ANDLW  00
00320:  MOVWF  00
00322:  MOVF   2A,W
00324:  ANDLW  0F
00326:  MOVWF  03
00328:  MOVFF  00,03
0032C:  MOVLW  00
0032E:  ADDWF  2F,F
00330:  MOVF   00,W
00332:  ADDWFC 30,F
00334:  MOVF   29,W
00336:  ANDLW  F0
00338:  MOVWF  00
0033A:  CLRF   03
0033C:  MOVWF  02
0033E:  RLCF   02,F
00340:  RLCF   03,F
00342:  RLCF   02,F
00344:  RLCF   03,F
00346:  RLCF   02,F
00348:  RLCF   03,F
0034A:  RLCF   02,F
0034C:  RLCF   03,F
0034E:  MOVLW  F0
00350:  ANDWF  02,F
00352:  MOVF   02,W
00354:  ADDWF  2F,F
00356:  MOVF   03,W
00358:  ADDWFC 30,F
0035A:  MOVF   29,W
0035C:  ANDLW  0F
0035E:  MOVWF  00
00360:  CLRF   03
00362:  MOVF   00,W
00364:  ADDWF  2F,W
00366:  MOVWF  2D
00368:  MOVF   03,W
0036A:  ADDWFC 30,W
0036C:  MOVWF  2E
....................    //and k is the final result


...51 instructions Smile) ...oh! wait! it's not 51, it might be 0x51, it's 81! ... Shocked lost between numbers Rolling Eyes Laughing
Ttelmah



Joined: 11 Mar 2010
Posts: 19539

View user's profile Send private message

PostPosted: Mon Dec 16, 2013 5:40 am     Reply with quote

Unfortunately though, won't work....

(h&0xF000), does not return '1', but '0x1000'. 4096. But the value this digit represents wants to only be 1000 decimal.....
Try it.
No wonder it involves a less work!. Actually gives the same number 'out' as 'in'....

It is the multiplications by 10, 100 etc., that makes the BCD conversion much harder than hex conversion.

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