11

How can I convert a binary-coded decimal number into a decimal number in terms of representation ? I don't want to convert the value of it but rather the representation of it, here is what I mean.

I want to convert 0x11 to decimal 11 (not 17) and 0x20 to 20 (not 32).

unsigned char day = 0x11;
unsigned char month = 0x12;

int dayDecimal, monthDecimal;

I want dayDecimal to be 11 and monthDecimal = 12. I will be working with a range between 0x00 to 0x60 so it should be possible. There won't be 'A', 'B', 'C', 'D', 'E', 'F.

Update:

I am actually reading time from an RTCC chip as part of an embedded project I am working on. The hours, minutes, day, and month are returned in that form. For example if minutes are 0x40 then it means 40 minutes and not 64, so I need to able to keep the interpretation of it correctly. I need somehow to convert 0x40 into 40 and not 64. I hope that's possible.

Thanks!

M.M
  • 138,810
  • 21
  • 208
  • 365
Ammar
  • 1,203
  • 5
  • 27
  • 64
  • `0x11` is a number literal. It's just a number; it's neither hex nor decimal. Hex is just a way to display a number. – SLaks Jan 25 '15 at 03:53
  • I hope this is just an academic exercise.... – sled Jan 25 '15 at 03:57
  • 4
    It is seeking to convert a BCD (Binary coded decimal) value into its decimal equivalent. It's a little unusual, but far from unheard of. Heck, Z80 chips had instructions to support BCD addition and subtraction. – Jonathan Leffler Jan 25 '15 at 04:11
  • 2
    @sled BCD used to be fairly common. obviously it wastes a small amount of storage space but it makes the encoded data more human-readable. For example if the timestamp is packed as BCD you can pick it out very quickly from the binary data dump of a message. – M.M Jan 25 '15 at 04:54
  • @MattMcNabb thanks for the insights - everyday you learn something new. – sled Jan 25 '15 at 17:07

3 Answers3

19

You need to work with the two nybbles, multiplying the more significant nybble by ten and adding the less significant:

uint8_t hex = 0x11;
assert(((hex & 0xF0) >> 4) < 10);  // More significant nybble is valid
assert((hex & 0x0F) < 10);         // Less significant nybble is valid
int dec = ((hex & 0xF0) >> 4) * 10 + (hex & 0x0F);

If the assertions are disabled but the input is bogus (e.g. 0xFF), you get what you deserve: GIGO — garbage in, garbage out. You can easily wrap that into an (inline) function:

static inline int bcd_decimal(uint8_t hex)
{
    assert(((hex & 0xF0) >> 4) < 10);  // More significant nybble is valid
    assert((hex & 0x0F) < 10);         // Less significant nybble is valid
    int dec = ((hex & 0xF0) >> 4) * 10 + (hex & 0x0F);
    return dec;
}       

This conversion is reminiscent of BCD — Binary Coded Decimal.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • I am not sure how you came up with uint8_t and assert, its not clear to me. By the way check the update to my question. – Ammar Jan 25 '15 at 04:01
  • 2
    On most systems, `uint8_t` (from ``) is equivalent to `unsigned char`. In fact, I think it is safe to say that if the system provides `uint8_t`, then it is equivalent to `unsigned char`, but the type is theoretically optional. If you don't like it, you can change the types to `unsigned char` without any change in functionality. The asserts are in place to make sure you're being honest about your claimed valid inputs. If you never provide bogus data, the assertions will never fire. If you don't like the assertions and you don't mind playing with fire, you can remove the assertions. – Jonathan Leffler Jan 25 '15 at 04:03
  • basically the code should look like this "unsigned hex = 0x60; int dec = ((hex & 0xF0) >> 4) * 10 + (hex & 0x0F);" and by the way I think you are right. I need to test it to confirm it. – Ammar Jan 25 '15 at 04:07
  • You are right, thank you. I would not have figured it out, I appreciate it. – Ammar Jan 25 '15 at 04:12
14

A very simple method without error checking:

int bcd_to_decimal(unsigned char x) {
    return x - 6 * (x >> 4);
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • 1
    Nice solution, it took me a minute to figure out the math. So, let's say we have: 0x34 == 3 * 0x10 + 4 == 3 * 16 + 4 == 3 * 10 + 3 * 6 + 4 == 30 + 3 * 6 + 4 == 34 + 3 * 6 == 34 + (0x34 >> 4) * 6 which gives us this: 34 == 0x34 - (0x34 >> 4) * 6 – scope Feb 14 '19 at 16:32
1

Put the desired value in the function and you will get an integer in return.

#include <stdio.h>
#include <math.h>

typedef int                 INT32;

typedef short int           INT16;

typedef unsigned short int  UINT16;

typedef unsigned long int   UINT32;

UINT32 BCDToDecimal(UINT32 nDecimalValue){
    UINT32 nResult=0;
    INT32  nPartialRemainder, ncnt,anHexValueStored[8];
    UINT16 unLengthOfHexString = 0,unflag=0;

    for(ncnt=7 ;ncnt>=0 ; ncnt--){
        anHexValueStored[ncnt]=nDecimalValue & (0x0000000f << 4*(7-ncnt));
        anHexValueStored[ncnt]=anHexValueStored[ncnt] >> 4*(7-ncnt);
        if(anHexValueStored[ncnt]>9)
        unflag=1;
    }
    if(unflag==1){
        return 0;
    }
    else{
        for(ncnt=0 ;ncnt<8 ; ncnt++)
        nResult= nResult +anHexValueStored[ncnt]*pow(10,(7-ncnt));
        return nResult;
    }
}
int main() {
    printf("%ld\n",BCDToDecimal(0X20));
    return 0;
}
tuomastik
  • 4,559
  • 5
  • 36
  • 48