6

I'm to stupid right now to solve this problem...

I get a BCD number (every digit is an own 4Bit representation)

For example, what I want:

  • Input: 202 (hex) == 514 (dec)
  • Output: BCD 0x415

  • Input: 0x202

  • Bit-representation: 0010 0000 0010 = 514

What have I tried:

unsigned int uiValue = 0x202;
unsigned int uiResult = 0;
unsigned int uiMultiplier = 1;
unsigned int uiDigit = 0;


// get the dec bcd value
while ( uiValue > 0 )
{
    uiDigit= uiValue & 0x0F;
    uiValue >>= 4;
    uiResult += uiMultiplier * uiDigit;
    uiMultiplier *= 10;
}

But I know that's very wrong this would be 202 in Bit representation and then split into 5 nibbles and then represented as decimal number again

I can solve the problem on paper but I just cant get it in a simple C-Code

Daniel Gehriger
  • 7,339
  • 2
  • 34
  • 55
Sagi
  • 783
  • 1
  • 8
  • 17
  • 1
    Can I suggest that, when dealing with bytes and nibbles, hex masks are easier to put into context? eg 0x0F is more obvious that 15 (at least to me!) – Andrew Nov 06 '12 at 09:10
  • I have added some more informations – Sagi Nov 06 '12 at 09:10
  • Your code seems just fine to me. decimal 202 is binary 1100 1010, or, split into nibbles: decimal 6 4 and therefore the BCD representation of decimal 64. – Daniel Gehriger Nov 06 '12 at 09:11
  • 202 hex = 514 dec = 514 BCD :) – Andrew Nov 06 '12 at 09:12
  • 1
    @Andrew: But he said `uiValue = 202`, not `0x202...`. And if it was 0x202, then the BCD value would be decimal 202... – Daniel Gehriger Nov 06 '12 at 09:13
  • @DanielGehriger: thats true, I also came to this point in my tryings – Sagi Nov 06 '12 at 09:18
  • 1
    @Sagi: Hence my answer below... – Daniel Gehriger Nov 06 '12 at 09:19
  • I must be going mad, because I thought the clue was in the name: Binary Coded DECIMAL not BChex :) – Andrew Nov 06 '12 at 09:34
  • Sagi - can you please clarify: Are you wanting to convert BCD to Uint (question title), or Uint to BCD (code) – Andrew Nov 06 '12 at 09:35
  • @Sagi, there's ongoing discussion in the comments to my answer on whether you actually want a BCD as your output (as your question states) or a regular int (as your question title does and also the first revision of your question did). Please provide binary representation of correct result that you want for input 0010 0000 0010 – panda-34 Nov 06 '12 at 09:36

10 Answers10

14

You got it the wrong way round. Your code is converting from BCD to binary, just as your question's (original) title says. But the input and output values you provided are correct only if you convert from binary to BCD. In that case, try:

#include <stdio.h>

int main(void) {

   int binaryInput = 0x202; 
   int bcdResult = 0;
   int shift = 0;

   printf("Binary: 0x%x (dec: %d)\n", binaryInput , binaryInput );

   while (binaryInput > 0) {
      bcdResult |= (binaryInput % 10) << (shift++ << 2);
      binaryInput /= 10;
   }

   printf("BCD: 0x%x (dec: %d)\n", bcdResult , bcdResult );
   return 0;
}

Proof: http://ideone.com/R0reQh

Daniel Gehriger
  • 7,339
  • 2
  • 34
  • 55
  • Thanks, thats what I needed :) it's nearly the same code as I had, but I went the wrong direction... Thanks – Sagi Nov 06 '12 at 09:43
  • Sorry, my previous comment is in error but I can't remove it. – PapaAtHome Oct 22 '13 at 08:13
  • Thanks a lot, this has helped me. One small note: not sure if this was your objective, but the output BCD nibbles come out ordered "least significant first". In my case, I wanted the nibbles to come out "most significant first", so I had to shift each nibble left by an incrementing multiple of 4 (shift just after taking the modulo) and only then OR the pre-shifted nibble to the result. Note how the algorithm (%10, /=10) starts from the lowest-order decimal digits and proceeds to higher orders. – frr May 16 '17 at 12:50
  • @frr - you are of course correct. It was a simple mistake, and I fixed the code. Thanks for reporting! – Daniel Gehriger May 17 '17 at 19:49
  • 1
    For platforms which have a high cost to division instructions (arguably, all of them), you can restructure the `while (binaryInput > 0)` to a `while (binaryInput > 9)` and add any remainder up to 9 in binaryInput to the final value without dividing again. – thomasrutter Aug 22 '21 at 12:44
2

Try the following.

unsigned long toPackedBcd (unsigned int val)
{
  unsigned long bcdresult = 0; char i;


  for (i = 0; val; i++)
  {
    ((char*)&bcdresult)[i / 2] |= i & 1 ? (val % 10) << 4 : (val % 10) & 0xf;
    val /= 10;
  }
  return bcdresult;
}

Also one may try the following variant (although maybe little inefficient)

/*
Copyright (c) 2016 enthusiasticgeek<enthusiasticgeek@gmail.com> Binary to Packed BCD
This code may be used (including commercial products) without warranties of any kind (use at your own risk)
as long as this copyright notice is retained.
Author, under no circumstances, shall not be responsible for any code crashes or bugs.
Exception to copyright code: 'reverse string function' which is taken from http://stackoverflow.com/questions/19853014/reversing-a-string-in-place-in-c-pointers#19853059
Double Dabble Algorithm for unsigned int explanation

255(binary) - base 10 -> 597(packed BCD) - base 16
     H|    T|    U|        (Keep shifting left)
               11111111
             1 1111111
            11 111111  
           111 11111
          1010 11111    <-----added 3 in unit's place (7+3 = 10) 
        1 0101 1111  
        1 1000 1111     <-----added 3 in unit's place (5+3 = 8)
       11 0001 111
      110 0011 11       
     1001 0011 11       <-----added 3 in ten's place (6+3 = 9)
   1 0010 0111 1  
   1 0010 1010 1        <-----added 3 in unit's place (7+3 = 10)
  10 0101 0101  -> binary 597 but bcd 255
  ^    ^    ^  
  |    |    |
  2    5    5   
*/
#include <stdio.h>   
#include <string.h>

//Function Prototypes
unsigned int binaryToPackedBCD (unsigned int binary); 
char * printPackedBCD(unsigned int bcd, char * bcd_string);

// For the following function see http://stackoverflow.com/questions/19853014/reversing-a-string-in-place-in-c-pointers#19853059
void reverse(char *str);

//Function Definitions
unsigned int binaryToPackedBCD (unsigned int binary) {
  const unsigned int TOTAL_BITS = 32;
  /*Place holder for bcd*/
  unsigned int bcd = 0;
  /*counters*/
  unsigned int i,j = 0;
  for (i=0; i<TOTAL_BITS; i++) {
     /*
      Identify the bit to append  to LSB of 8 byte or 32 bit word -
      First bitwise AND mask with 1. 
      Then shift to appropriate (nth shift) place. 
      Then shift the result back to the lsb position. 
     */
      unsigned int binary_bit_to_lsb = (1<<(TOTAL_BITS-1-i)&binary)>>(TOTAL_BITS-1-i);
      /*shift by 1 place and append bit to lsb*/
      bcd = ( bcd<<1 ) | binary_bit_to_lsb;       
      /*printf("=> %u\n",bcd);*/
      /*Don't add 3 for last bit shift i.e. in this case 32nd bit*/
      if( i >= TOTAL_BITS-1) { 
      break;
      }
      /*else continue*/
      /* Now, check every nibble from LSB to MSB and if greater than or equal 5 - add 3 if so */
      for (j=0; j<TOTAL_BITS; j+=4) {
        unsigned int temp = (bcd & (0xf<<j))>>j;
        if(temp >= 0x5) {
        /*printf("[%u,%u], %u, bcd = %u\n",i,j, temp, bcd);*/
        /*Now, add 3 at the appropriate nibble*/
         bcd = bcd  + (3<<j);
        // printf("Now bcd = %u\n", bcd);
        }
      }
  }
  /*printf("The number is %u\n",bcd);*/
  return bcd;
}   

char * printPackedBCD(unsigned int bcd, char * bcd_string) {
  const unsigned int TOTAL_BITS = 32;
  printf("[LSB] =>\n");
   /* Now, check every nibble from LSB to MSB and convert to char* */
  for (unsigned int j=0; j<TOTAL_BITS; j+=4) {
  //for (unsigned int j=TOTAL_BITS-1; j>=4; j-=4) {
      unsigned int temp = (bcd & (0xf<<j))>>j;
      if(temp==0){
    bcd_string[j/4] = '0';      
      } else if(temp==1){
    bcd_string[j/4] = '1';
      } else if(temp==2){
    bcd_string[j/4] = '2';
      } else if(temp==3){
    bcd_string[j/4] = '3';
      } else if(temp==4){
    bcd_string[j/4] = '4';
      } else if(temp==5){
    bcd_string[j/4] = '5';
      } else if(temp==6){
    bcd_string[j/4] = '6';
      } else if(temp==7){
    bcd_string[j/4] = '7';
      } else if(temp==8){
    bcd_string[j/4] = '8';
      } else if(temp==9){
    bcd_string[j/4] = '9';
      } else {
    bcd_string[j/4] = 'X';
      }
      printf ("[%u - nibble] => %c\n", j/4, bcd_string[j/4]);
  }      
  printf("<= [MSB]\n");
  reverse(bcd_string);
  return bcd_string;
}

// For the following function see http://stackoverflow.com/questions/19853014/reversing-a-string-in-place-in-c-pointers#19853059
void reverse(char *str)
{ 
    if (str != 0 && *str != '\0') // Non-null pointer; non-empty string
    {
    char *end = str + strlen(str) - 1; 
    while (str < end)
    {
        char tmp = *str; 
        *str++ = *end; 
        *end-- = tmp;
    } 
    }
}

int main(int argc, char * argv[])
{
  unsigned int number = 255;
  unsigned int bcd = binaryToPackedBCD(number);
  char bcd_string[8];
  printPackedBCD(bcd, bcd_string);
  printf("Binary (Base 10) = %u => Packed BCD (Base 16) = %u\n OR \nPacked BCD String = %s\n", number, bcd, bcd_string);
  return 0;
}
enthusiasticgeek
  • 2,640
  • 46
  • 53
  • I test this function and it appears to work initially, but I haven't tried it for very large numbers or compared its efficiency with outher candidate algorithm to do the conversion from e.g. `11` to `17` or likewise. (My project is C code for the 7-segment LEDs of the Altera DE2 FPGA.) – Niklas Rosencrantz Mar 30 '14 at 11:01
1

The real problem here is confusion of bases and units

The 202 should be HEX which equates to 514 decimal... and therefore the BCD calcs are correct

Binary code decimal will convert the decimal (514) into three nibble sized fields: - 5 = 0101 - 1 = 0001 - 4 = 0100

The bigger problem was that you have the title the wrong way around, and you are converting Uint to BCD, whereas the title asked for BCD to Unint

Andrew
  • 2,046
  • 1
  • 24
  • 37
  • Andrew, it's not as complicated as that. No need to convert from hex to decimal to find the BCD value. – Daniel Gehriger Nov 06 '12 at 09:20
  • NO, but I think it explains the confusion... 0x202 == 514(bcd) whereas 202(dec) == 202(bcd) – Andrew Nov 06 '12 at 09:23
  • @Andrew It's the other way, 0x202 == 202 (bcd). – nos Nov 06 '12 at 09:33
  • Eh? Lets go simpler... 10d = 0Ah = (0001 0000)BCD == "10"bcd not "0A"bcd... so how can 0x202 == 202 BCD? – Andrew Nov 06 '12 at 09:37
  • @Andrew 10d = 0Ah has a binary representation of 0000 1010 , so I guess it depends on whether his 202 means bcd, decimal or hex. Certainly `uiValue = 202` is wrong, though the text says that it should be hex: `Input: 202 (hex)` If the input is 0x202 as the text says, the binary representaion of the input is 0000 0010 0000 0010, which we can convert easily to bcd. – nos Nov 06 '12 at 09:39
  • 10 == 0x0A has a **binary** representation of 1010 yes, but a **BINARY CODED DECIMAL** of 0001-0000. The two are NOT the same – Andrew Nov 06 '12 at 09:40
1

My 2 cents, I needed similar for a RTC chip which used BCD to encode the time and date info. Came up with the following macros that worked fine for the requirement:

#define MACRO_BCD_TO_HEX(x) ((BYTE) ((((x >> 4) & 0x0F) * 10) + (x & 0x0F)))

#define MACRO_HEX_TO_BCD(x) ((BYTE) (((x / 10 ) << 4) | ((x % 10))))

0

A naive but simple solution:

char buffer[16];
sprintf(buffer, "%d", var);
sscanf(buffer, "%x", &var);
panda-34
  • 4,089
  • 20
  • 25
  • Isn't this going to produce hex, where as the OP asked for BCD? They are not equivalent. I didn't down vote though. It's some body else – fkl Nov 06 '12 at 09:19
  • From the first revision of his question it was plain that he wanted to convert 202 to 514, It does just that. – panda-34 Nov 06 '12 at 09:20
  • But he wants 514 BCD not 514 Hex – Andrew Nov 06 '12 at 09:26
  • @Andrew, he started wanting that after my answer, I answered the first revision, where he stated that only input was in BCD – panda-34 Nov 06 '12 at 09:29
  • OK although his title always said BCD... but then again his question title looks wrong for the question – Andrew Nov 06 '12 at 09:31
  • 2
    @nos, I don't know, should I answer the question in the title, in the question body or in the sample code. They're three different ones. – panda-34 Nov 06 '12 at 09:45
  • @panda-34: personally, I like your solution - and it turns out to be correct, given the OPs comment to his accepted answer. – Daniel Gehriger Nov 06 '12 at 09:49
0

This is the solution that I developed and works great for embedded systems, like Microchip PIC microcontrollers:

#include <stdio.h>
void main(){
    unsigned int output = 0;
    unsigned int input;
    signed char a;
    //enter any number from 0 to 9999 here:
    input = 1265;
    for(a = 13; a >= 0; a--){
        if((output & 0xF) >= 5)
            output += 3;
        if(((output & 0xF0) >> 4) >= 5)
            output += (3 << 4);
        if(((output & 0xF00) >> 8) >= 5)
            output += (3 << 8);
        output = (output << 1) | ((input >> a) & 1);
    }
    printf("Input decimal or binary: %d\nOutput BCD: %X\nOutput decimal: %u\n", input, output, output);
}
0

This is my version for a n byte conversion:

//----------------------------------------------
// This function converts n bytes Binary (up to 8, but can be any size)
// value to n bytes BCD value or more.
//----------------------------------------------

void bin2bcdn(void * val, unsigned int8 cnt)
{
    unsigned int8  sz, y, buff[20];         // buff = malloc((cnt+1)*2);
    
    if(cnt > 8) sz = 64;                    // 8x8
    else        sz = cnt * 8 ;              // Size in bits of the data we shift
    
    memset(&buff , 0, sizeof(buff));        // Clears buffer
    memcpy(&buff, val, cnt);                // Copy the data to buffer

    while(sz && !(buff[cnt-1] & 0x80))      // Do not waste time with null bytes,
    {                                       // so search for first significative bit
        rotate_left(&buff, sizeof(buff));   // Rotate until we find some data
        sz--;                               // Done this one
    }
    while(sz--)                             // Anyting left?
    {
        for( y = 0; y < cnt+2; y++)         // Here we fix the nibbles
        {
            if(((buff[cnt+y] + 0x03) & 0x08) != 0) buff[cnt+y] += 0x03;
            if(((buff[cnt+y] + 0x30) & 0x80) != 0) buff[cnt+y] += 0x30;
        }
        rotate_left(&buff, sizeof(buff));   // Rotate the stuff
    }
    memcpy(val, &buff[cnt], cnt);           // Copy the buffer to the data
//  free(buff);       //in case used malloc
}   // :D Done
0
long bin2BCD(long binary) { // double dabble: 8 decimal digits in 32 bits BCD
  if (!binary) return 0;
  long bit = 0x4000000; //  99999999 max binary
  while (!(binary & bit)) bit >>= 1;  // skip to MSB

  long bcd = 0;
  long carry = 0;
  while (1) {
    bcd <<= 1;
    bcd += carry; // carry 6s to next BCD digits (10 + 6 = 0x10 = LSB of next BCD digit)
    if (bit & binary) bcd |= 1;
    if (!(bit >>= 1)) return bcd;
    carry = ((bcd + 0x33333333) & 0x88888888) >> 1; // carrys: 8s -> 4s
    carry += carry >> 1; // carrys 6s  
  }
}
0

Simple solution

#include <stdio.h>

int main(void) {

   int binaryInput = 514 ;      //0x202 
   int bcdResult = 0;
   int digit = 0;
   int i=1;

   printf("Binary: 0x%x (dec: %d)\n", binaryInput , binaryInput );

   while (binaryInput > 0) {
 
      digit = binaryInput %10;          //pick digit
      bcdResult = bcdResult+digit*i;
      i=16*i;
      binaryInput = binaryInput/ 10;
   }
   printf("BCD: 0x%x (dec: %d)\n", bcdResult , bcdResult );
   return 0;
}

Binary: 0x202 (dec: 514)

BCD: 0x514 (dec: 1300)

0

You can also try the following:

In every iteration the remainder ( represented as a nibble ) is positioned in its corresponding place.

uint32_t bcd_converter(int num)
{                          
  uint32_t temp=0;              
  int i=0;                 
  while(num>0){            
    temp|=((num%10)<<i);   
    i+=4;                  
    num/=10;               
  }                        
                           
  return temp;             
}                          

NAND
  • 663
  • 8
  • 22