I needed to convert some numbers received from a PLC from their original packed BCD format to binary (hex) format. The numbers to convert are split into two 16-bit halves, and need to be converted into one 32-bit number.
(Packed BCD values contain two digits for every byte.)
There is a method that exists that will convert numbers from BCD into a true hex number, by using string formatting, but this is relatively expensive in terms of performance and I required the best performance possible.
So, I came up with this function:
1: /// <summary>
2: /// Convert two PLC words in BCD format (forming 8 digit number) into single binary integer.
3: /// e.g. If Lower = 0x5678 and Upper = 0x1234, then Return is 12345678 decimal, or 0xbc614e.
4: /// </summary>
5: /// <param name="lower">Least significant 16 bits.</param>
6: /// <param name="upper">Most significant 16 bits.</param>
7: /// <returns>32 bit unsigned integer.</returns>
8: /// <remarks>If the parameters supplied are invalid, returns zero.</remarks>
9: private uint BCD2ToBin(uint lower, uint upper)
10: {
11: uint binVal = 0;
12:
13: if ((lower | upper) != 0)
14: {
15: int shift = 0;
16: uint multiplier = 1;
17: uint bcdVal = (upper << 16) | lower;
18:
19: for (int i = 0; i < 8; i++)
20: {
21: uint digit = (bcdVal >> shift) & 0xf;
22:
23: if (digit > 9)
24: {
25: binVal = 0;
26: break;
27: }
28: else
29: {
30: binVal += digit * multiplier;
31: shift += 4;
32: multiplier *= 10;
33: }
34: }
35: }
36:
37: return binVal;
38: }
The function takes in two unsigned itegers that hold each of the 16-bit weighted BCD values, and returns a 32-bit unsigned integer.
It is important to note that the return is an unsigned integer, as this allows BCD values up to 0x99999999 to be converted correctly.
I have also taken the approach of returning zero if any of the values supplied are incorrect, i.e. contain an invalid digit. (BCD values can only contain digits ranging between 0 and 9, and not A to F.)
Of course, it may be desirable to return a different value, or to throw an exception if the parameters supplied are invalid, but for my purposes, returning a zero was sufficient.
This method proved to be around four to five times quicker than by using the string format method.