How to convert Binary Coded Decimal to int in C++

3.8k views Asked by At

Given a hex value of 0x80 (supplied as a uint16_t) in c++ how would I extract the digits into an int variable. I don't want to convert the hex to int, rather I want to extract the digits as an int, so for example, for 0x80 i want an int of 80, not 128, or for 0x55 I want 55 not 85, etc.

uint16_t hex = 0x80;
uint8_t val = ???; // Needs to be 80

Numbers with hex-only digits will never happen. I.e. input will only consist of decimal digits.
E.g. 0x08, 0x09, 0x10, 0x11, 0x12, etc.

4

There are 4 answers

7
Support Ukraine On BEST ANSWER

Since you say that the hex number never contains 'a', 'b', etc. this code should do the trick:

#include <iostream>

int main() {
    uint16_t in = 0x4321;

    int t = (1000 * ((in & 0xf000) / (16*16*16))) + 
             (100 * ((in & 0xf00) / (16*16))) + 
              (10 * ((in & 0xf0) / 16)) + 
               (1 * ((in & 0xf) / 1));
    std::cout << t << std::endl;
    return 0;
}

output

4321

Explanation

A 16 bit hex number in = 0xWZYX is calculated as

in = W*16^3 + Z*16^2 + Y*16^1 + X*16^0 (or just W*16^3 + Z*16^2 + Y*16^1 + X)

When doing

in & 0xf000 you get 0xW000

when doing

0xW000 / 16^3 you get 0x000W or just W

When doing

1000 * W you get W000 decimal

The pattern is then repeated for each digit.

An alternative implementation using shift

#include <iostream>

int main() {
    uint16_t in = 0x9321;

    int t = (1000 * ((in & 0xf000) >> 12)) + 
             (100 * ((in & 0xf00) >> 8)) + 
              (10 * ((in & 0xf0) >> 4)) + 
               (1 * ((in & 0xf) >> 0));
    std::cout << t << std::endl;
    return 0;
}

For a 16 bit unsigned integer it can be okay to write out the four lines. However, if you wanted a similar function for a larger unsigned int, it may be better to do a loop to keep the code more compact and maintainable.

64 bit solution using a loop

#include <iostream>

int64_t directHexToDec(uint64_t in)
{
    int64_t res = 0;
    uint64_t mask = 0xf;
    uint64_t sh = 0;
    uint64_t mul = 1;
    for (int i=0; i<16; ++i)
    {
        res += mul * ((in & mask) >> sh);
        mul *= 10;
        mask <<= 4;
        sh += 4;
    }
    return res;
}

int main() {
    uint64_t in = 0x987654321;

    int64_t t = directHexToDec(in);
    std::cout << t << std::endl;
    return 0;
}

output

987654321
4
leyanpan On

A simple implementation as follows:

int from_hex(uint16_t h)
{
    int d = 0;
    int power = 1;
    while (h)
    {
        // assert(h % 16 < 10)
        d += h % 16 * power;
        h /= 16;
        power *= 10;
    }
    return d;
}

And thanks to Minor Threat for the following:

int from_hex(uint16_t h)
{
    int d = 0;
    int power = 1;
    while (h)
    {
        // assert(h % 16 < 10)
        d += (h & 15) * power;
        h >>= 4;
        power *= 10;
    }
    return d;
}

I'm glad to know if something's wrong with my code

0
Ervin Szilagyi On

A simpler answer using streams and string conversions. Please note, this wont work in case of 0x0A, 0X0B, ... values.

uint16_t val = 0x80;
std::stringstream stream;
stream << std::hex << val;
std::string resultStr(stream.str());
// In case of 0xYY hex value be carefull that YY fits into an uint8_t, 
// otherwise  this will overflow
uint8_t result = static_cast<uint8_t>(std::stoi(resultStr));
1
sergej On

This should work for hex <= 0x99:

#include <iostream>

unsigned int foo(unsigned int hex)
{
    return  hex - (hex >> 4) * 6;
}

/* Test */
int main()
{
    unsigned int vals[] = {0x08, 0x09, 0x10, 0x11, 0x12, 0x80};

    for (int i = 0; i < sizeof(vals) / sizeof(vals[0]); i++)
        std::cout << foo(vals[i]) << std::endl;

    return 0;
}

Result:

8
9
10
11
12
80