How to convert decimal back BCD? - c

I am able to convert BCD to decimal, for example I can convert 0x11 to 11 instead of 17 in decimal. This is the code I used.
unsigned char hex = 0x11;
unsigned char backtohex ;
int dec = ((hex & 0xF0) >> 4) * 10 + (hex & 0x0F);
Now I want to convert dec back to BCD representation. I want 11 to be converted back to 0x11 not 0x0B. I am kind of confused as how to go back.
Thanks!

Assuming your input is always between 0 and 99, inclusive:
unsigned char hex = ((dec / 10) << 4) | (dec % 10);
Simply take the upper digit and shift it left by one nibble, and or the lower digit in place.

Related

c: convert hex to same value decimal number

i have
UINT8 year = 0x15;
and I want to get decimal value 15 in UINT8 decYear from year. How can I do it ? thankyou
background: I am ready day, month, year. I get value back 0x15, 0x10, 0x13 respectively for today. and I want to convert them to decimal values
The representation you are using is called Binary Coded Decimal and it's an old way of encoding values.
To get a "proper" decimal value you need to know how binary values work, and how to use the bitwise operators in C.
Lets say you have the BCD encoded value 0x15, this is binary 00010101. As you can see the 1 is stored in the high nibble and the 5 in the low. Getting them out by themselves is an easy bitwise-and operation:
int year = 0x15;
int high_nibble = year & 0xf0; // Gets the high nibble, i.e. `0x10`
int low_nibble = year & 0x0f; // Gets the low nibble, i.e. `0x05`
Now you have two variables, one containing 0x10 and the other 0x05. The hexadecimal value 0x05 is the same as the decimal value 5, so nothing needs to be done with it. The other value needs some work to make it the decimal 10. For this we simply shift it down four bits (00010000 will become 00000001) doing
high_nibble >> 4
and to "convert" it to decimal you multiply the value by 10 (1 * 10 == 10), and finally add the 5.
To put it all together into a single expression:
int decYear = ((year & 0xf0) >> 4) * 10 + (year & 0x0f);
In this specific case you can use this
UINT8 year = 0x15;
UINT8 yearD = year%16+ (year/16)*10;
There's better solution using BCD encoding
UINT8 yearD = ((year>>4)&0x0f)*10+ (year&0x0f);
Explanation:
Binary representation of 0x15 is ‭00010101‬. We have high nibble with 1 (0001) and low nibble 5(0101). So final result is higher nibble value *10 + value of lower nibble. To get high nibble value we do 4 shift right then bitwise AND with 0x0f, 4 right shift take high nibble value to lower nibble and bitwise and with 0x0f clear upper nibble, which is the real value of upper nibble. And to get lower nibble value we need to clear higher nibble value to do so we use year&0x0f.
Firstly: 0x15 is 21 (5 x 16 ^ 0) + ( 1 x 16 ^ 1 ), not 15.
Secondly, try:
UINT8 year = 0x15;
printf("%d",year);
This is because, no value is saved as hex or decimal, but as binary, ultimately. The hex you used is but a representation for your system to be translated to binary, like 21 would be.
Making it output 15 would probably be very difficult and make the fact that you even entered it in hex superflous. Its like entering 21 and asking for us to contrive a way for the system to know you meant 15
If you get a value in hex as return from something, try converting it into a string and just cutting off the 0x part of it.
For example, if you get 0x15 as value, just read it in as string, and cut the 0x off with string operations.
A "hexadecimal value that contains no letters" is Binary Coded Decimal (BCD). You can convert BCD to decimal like this
#include <stdio.h>
#include <stdint.h>
uint8_t bcd2dec(uint8_t bcdval)
{
return (bcdval >> 4) * 10 + (bcdval & 0xF);
}
int main(int argc, char *argv[])
{
printf("Day %u\n", bcd2dec(0x13));
printf("Month %u\n", bcd2dec(0x10));
printf("Year %u\n", bcd2dec(0x15));
return 0;
}
Program output:
Day 13
Month 10
Year 15
So if I understand correctly, you want to get 15 from 0x15.
You can use the following method:
char buffer[4];
UINT8 year = 0x15;
sprintf(buffer, "%x", year);
if you print buffer, you get "15" as a string. You can then do:
UINT8 decYear = atoi(buffer);
If 0x15 (in hexadecimal) does represent 15 (in decimal) then you are likely dealing with input in BCD format.
In such a case converting from BCD to decimal(1) is done by extracting each nible of the input and adding them properly.
Our input is a byte of 8 bits.
The 4 less significative bits (the lower nibble) represent the units, a number from 0 to 9. Values from A to F (hexadecimal) are never used.
The 4 most significative bits (the upper nibble) represent the tens. Also a number from 0 to 9.
If you extract them you get the decimal(1) value by the formula : tens*10 + units.
We can get the lower nibble with a binary mask : year & 0x0F
And the upper nibble with a right shift and the same binary mask : (year>>4) & 0x0F
The whole code is :
UINT8 year = 0x15;
assert ( ( (year>>4) & 0x0F ) < 10 ); // Ensure top nibble correctness
assert ( (year & 0x0F) < 10); // Ensure low nibble correcness
int yearBin = ( (year>>4) & 0x0F ) * 10 + (year&0x0F);
printf("%d\n", (int)yearBin); // Print yearBin in decimal : 15 will be printed
printf("%x\n", (int)yearBin); // Print yearBin in hexadecimal :
// 0x15 will not be printed. 0xF will be printed.
(1) Actually, we are not converting from BCD to decimal. We are converting from BCD to binary (most likely in 2-complement). It is the choice of conversion parameters in printf what makes it be printed in decimal.

Checking byte with bitwise operators

I could use this:
unsigned long alpha = 140 | 130 << 8 | 255 << 16;
to set 140 to the first byte of alpha, 130 to the second and 255 as 3rd.
How do I do the opposite (i.e checking a specific byte of alpha) ?
alpha & 255 // works for the first byte
alpha >> 16; // works for the 3rd byte
Shift the value x bits to the right and then use AND to restrict the number of bits you use. ie: (n >> 8) & 0xff or (n >> 16) & 0xff.

How to convert BCD to decimal?

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!
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.
A very simple method without error checking:
int bcd_to_decimal(unsigned char x) {
return x - 6 * (x >> 4);
}
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;
}

How do I extract bits from 32 bit number

I have do not have much knowledge of C and I'm stuck with a problem since one of my colleague is on leave.
I have a 32 bit number and i have to extract bits from it. I did go through a few threads but I'm still not clear how to do so. I would be highly obliged if someone can help me.
Here is an example of what I need to do:
Assume hex number = 0xD7448EAB.
In binary = 1101 0111 0100 0100 1000 1110 1010 1011.
I need to extract the 16 bits, and output that value. I want bits 10 through 25.
The lower 10 bits (Decimal) are ignored. i.e., 10 1010 1011 are ignored.
And the upper 6 bits (Overflow) are ignored. i.e. 1101 01 are ignored.
The remaining 16 bits of data needs to be the output which is 11 0100 0100 1000 11 (numbers in italics are needed as the output).
This was an example but I will keep getting different hex numbers all the time and I need to extract the same bits as I explained.
How do I solve this?
Thank you.
For this example you would output 1101 0001 0010 0011, which is 0xD123, or 53,539 decimal.
You need masks to get the bits you want. Masks are numbers that you can use to sift through bits in the manner you want (keep bits, delete/clear bits, modify numbers etc). What you need to know are the AND, OR, XOR, NOT, and shifting operations. For what you need, you'll only need a couple.
You know shifting: x << y moves bits from x *y positions to the left*.
How to get x bits set to 1 in order: (1 << x) - 1
How to get x bits set to 1, in order, starting from y to y + x: ((1 << x) -1) << y
The above is your mask for the bits you need. So for example if you want 16 bits of 0xD7448EAB, from 10 to 25, you'll need the above, for x = 16 and y = 10.
And now to get the bits you want, just AND your number 0xD7448EAB with the mask above and you'll get the masked 0xD7448EAB with only the bits you want. Later, if you want to go through each one, you'll need to shift your result by 10 to the right and process each bit at a time (at position 0).
The answer may be a bit longer, but it's better design than just hard coding with 0xff or whatever.
OK, here's how I wrote it:
#include <stdint.h>
#include <stdio.h>
main() {
uint32_t in = 0xd7448eab;
uint16_t out = 0;
out = in >> 10; // Shift right 10 bits
out &= 0xffff; // Only lower 16 bits
printf("%x\n",out);
}
The in >> 10 shifts the number right 10 bits; the & 0xffff discards all bits except the lower 16 bits.
I want bits 10 through 25.
You can do this:
unsigned int number = 0xD7448EAB;
unsigned int value = (number & 0x3FFFC00) >> 10;
Or this:
unsigned int number = 0xD7448EAB;
unsigned int value = (number >> 10) & 0xFFFF;
I combined the top 2 answers above to write a C program that extracts the bits for any range of bits (not just 10 through 25) of a 32-bit unsigned int. The way the function works is that it returns bits lo to hi (inclusive) of num.
#include <stdio.h>
#include <stdint.h>
unsigned extract(unsigned num, unsigned hi, unsigned lo) {
uint32_t range = (hi - lo + 1); //number of bits to be extracted
//shifting a number by the number of bits it has produces inconsistent
//results across machines so we need a special case for extract(num, 31, 0)
if(range == 32)
return num;
uint32_t result = 0;
//following the rule above, ((1 << x) - 1) << y) makes the mask:
uint32_t mask = ((1 << range) -1) << lo;
//AND num and mask to get only the bits in our range
result = num & mask;
result = result >> lo; //gets rid of trailing 0s
return result;
}
int main() {
unsigned int num = 0xd7448eab;
printf("0x%x\n", extract(num, 10, 25));
}

Convert decimal to char/string

Let's say i have this number
int x = 65535;
Which is the decimal representation of:
ÿÿ
I know how i can do it from single char
#include <stdio.h>
int main() {
int f = 65535;
printf("%c", f);
}
But this will only give me "ÿ"
I would like to do this without using any external library, and preferably using C type strings.
#include <stdio.h>
int main() {
unsigned f = 65535; // initial value
// this will do the printf and ff >>= 8 until f <= 0 ( =0 actually)
do {
printf("%c", f & 0xff); // print once char. The &0xff keeps only the bits for one byte (8 bits)
f >>= 8; // shifts f right side for 8 bits
} while (f > 0);
}
Consider the value 65535, or 0xffff in hexadecimal, meaning its positive value takes 2 bytes that are 0xff and 0xff
print of f & 0xff keeps only the 8 LSb, (0xffff & 0xff = 0xff)
f >> = 8 shifts the value 8 bits to the right, 0xffff becomes 0x00ff (the 'ff' right side are gone
f > 0 is true since f == 0xff now
Next loop is the same, but f >>= 8 shifts 0x00ff to the right => 0x0000, and f is null.
Thus the f > 0 condition is wrong and the loop ends.
What you're looking for is bit masking: (x >> 8) & 0xFF for
the high order byte, and (x & 0xFF) for the lower. (Actually,
if int has 32 bits, it's (x >> 24) & 0xFF for the high order
byte. But given the values, and what you say your expecting,
you probably want the second byte, and not the high order byte.)
What you have is a 16 bit (two bytes) unsigned number. A char is 8 bits (one byte). This means you have to extract the two bytes in the number to get them as separate character.
This is done with the bitwise operators. You can use bitwise and & and bitwise shift >> to accomplish that.
Something like
char buffer[9];
long value = 65535;
char *cur = buffer;
while (value > 0)
{
*cur++ = value % 0x100;
value /= 0x100;
}
*cur = 0;

Resources