Thursday 23 October 2014

C++ BCD Encoding Tutorial

What is Binary Coded Decimal?... Well, when I did my A-Level the book I read described it as encoding the numeric values 0 through 9 into 4 binary bits, and then shuffling them into bytes, so you can represent the values precisely, without worry of the bit representation (big or little endian) of the system in use, and using less memory...

How can it take less memory?... If I store perhaps, 123 that's going to take 3 bytes!  I could put that into 1 byte... Yes you could because a single byte will hold 0 to 255.

But, what if you represent an integer?  4 bytes, the most significant word (two bytes) maybe little or big-endian, with BCD you can program your machine to move the values back correctly.

Lets take a look at some code:


This is only the first part however, we're encoding 1 digit as 8 bits....

We can define this encoding and then load our bytes back in whatever computer architecture we want.  This is very useful to remember when we talk between different endianess machine, such as old macs, Amiga's, Atari ST's... Even Spacecraft!

But storing 0 through 9 into 8 bits is a little wasteful, how many bits can represent 0 through 9 easily?... How about a nibble, or 4 bits?...

To do this we nave to flipflop through each digit character in our loop, and we shift the current binary left 4 bits then mask append the new value as we go round the loop...


This saves us space and gives us the output:

Technically the final blue nibble should have been shifted 4 bits left, but I'll leave you to fix that up.

I hope this quick tutorial has you thinking about encoding and how it works to help make data more easily cross platform.

And if you don't know what "Endianess" is, go search for it, but think about communications, about older systems, about different processor types... and then their applications in different systems, historical systems support, emulators and games.

--- Full Source code below ---

#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <bitset>

typedef unsigned char byte;
typedef std::vector<byte> BCDEncoding;

BCDEncoding EncodeIntegerIntoNibbles(const int& p_Value)
{
    BCDEncoding l_result;

    // Convert our integer into a string
    std::ostringstream l_oss;
    l_oss << p_Value;
    std::string l_string = l_oss.str();

    byte l_Value;

    byte l_Binary;
    bool l_FlipFlop = false;

    // Iterate over each character of the number, and add them to the encoding
    for (std::string::iterator l_itr = l_string.begin();
        l_itr != l_string.end();
        ++l_itr)
    {
        char l_current = (*l_itr);
        switch (l_current)
        {
            case '0': l_Value =0; break;
            case '1': l_Value =1; break;
            case '2': l_Value =2; break;
            case '3': l_Value =3; break;
            case '4': l_Value =4; break;
            case '5': l_Value =5; break;
            case '6': l_Value =6; break;
            case '7': l_Value =7; break;
            case '8': l_Value =8; break;
            case '9': l_Value =9; break;     
        }

        // First cycle, we load the bottom 4
        // bits of our binary byte with the
        // value, and the top 4 bits with zero's...
        // So just assign the value
        if (!l_FlipFlop)
        {
            l_Binary = l_Value;
            l_FlipFlop = true;
        }
        else
        {
            // In the second cycle, we move the bottom 4 bits up
            // then mask the second value part with the binary mask
            // 00001111, so we get the new lower half value... 00001111
            // being 15...
            l_Binary <<= 4;     /// Move up 4 places
            l_Binary |= (l_Value & 15);     // Set binary lower mask to the value 4 bits
            l_FlipFlop = false;

            // Now, we have the completed binary (2 nibbles) for a byte
            // we can add to our encoding
            l_result.push_back(l_Binary);
        }
    }

    // Finally, if we were part way through a binary
    // digit encode, we need to add that digit
    if (l_FlipFlop)
    {
        l_result.push_back(l_Binary);
    }

    return l_result;
}

BCDEncoding EncodeInteger(const int& p_Value)
{
    BCDEncoding l_result;

    // Convert our integer into a string
    std::ostringstream l_oss;
    l_oss << p_Value;
    std::string l_string = l_oss.str();

    // Iterate over each character of the number, and add them to the encoding
    for (std::string::iterator l_itr = l_string.begin();
        l_itr != l_string.end();
        ++l_itr)
    {
        char l_current = (*l_itr);
        switch (l_current)
        {
            case '0': l_result.push_back(0); break;
            case '1': l_result.push_back(1); break;
            case '2': l_result.push_back(2); break;
            case '3': l_result.push_back(3); break;
            case '4': l_result.push_back(4); break;
            case '5': l_result.push_back(5); break;
            case '6': l_result.push_back(6); break;
            case '7': l_result.push_back(7); break;
            case '8': l_result.push_back(8); break;
            case '9': l_result.push_back(9); break;
        }
    }

    return l_result;
}

void PrintEncoding(BCDEncoding& p_Encoding)
{
    std::cout << "Encoding : Length [" << p_Encoding.size() << "]" << std::endl;
    int i = 0;
    for (BCDEncoding::iterator l_itr = p_Encoding.begin();
        l_itr != p_Encoding.end();
        ++l_itr)
    {
        std::cout << "[" << (i++) << "] = " << std::bitset<8>((*l_itr)) << std::endl;
    }
}

int main()
{
    BCDEncoding l_Enc = EncodeInteger(1);
    PrintEncoding(l_Enc);

    l_Enc = EncodeInteger(24);
    PrintEncoding(l_Enc);

    l_Enc = EncodeInteger(123);
    PrintEncoding(l_Enc);

    l_Enc = EncodeIntegerIntoNibbles(123);
    PrintEncoding(l_Enc);
    
}

No comments:

Post a Comment