Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Byte[] to BCD and BCD to INT

Tags:

arrays

c#

byte

bcd

I have a Hex file created by CashRegister Machine. I have to read this file in.

File uses formatting detailed below. It is like socket packets.

Code Data : 2 Byte
PLU Code Data: 7 Byte
Unit Price Data: 5 Byte
Quantity Data: 5 Byte
Total Amount Data: 5 Byte
PLU Name Data: 18 Byte
Tax Rate Data: 1 Byte
Length: 24 + 19 Byte

  • PLU code format is BCD
  • Unit price 1-9999999999 (BCD)
  • quantity 1-9999999999 (BCD last 3 numbers should be decimal)
  • total amount 1-9999999999 (BCD)

I read in the hex file with a binary reader and then insert int the Unit Price byte array.

byte[] bytes = { data[21], data[22], data[23], data[24], data[25] }; // BCD Byte Array

This array is Unit Price. But how can I then convert this number to decimal. And the information says that for quantity : BCD last number should be decimal--what does this mean? Thanks.

like image 833
xFireTR Avatar asked Jul 28 '12 12:07

xFireTR


4 Answers

A BCD number encodes a value from 0-9 into 4 bits. In packed BCD (probably what you're dealing with), a byte is used to contain two values 0-9, one in each nibble (4 bits) of the byte. To convert to an int, you have to do a little bit fiddling. For example, the following would convert an array of BCD bytes into an int, which can hold up to 9 digits. Use long if you have more than 9 bcd digit of input.

// assume byte[] bcds is input
int result = 0;
foreach(byte bcd in bcds) {
    result *= 100;
    result += (10 * (bcd >> 4));
    result += bcd & 0xf;
}

This assumes that each byte is stored as big-endian BCD, where the most significant digit is in the most significant nibble of the byte. This is what is described in the Wikipedia page for BCD as the more common implementation. If you are dealing with little-endian BCD, the conversion code within the for loop would be

    result *= 100;
    result += (10 * (bcd & 0xf));
    result += bcd >> 4;

You also need to ensure you have the correct endianness of your array, i.e., does the first byte in the array contain the most significant two digits, or the least significant two digits. For example, the number 123456 would fit into 3 bytes using packed BCD. Is 12 in byte[0] or byte[2]? You would need to adjust the loop above to reverse the order if your endianness is different than my assumption. I'm assuming 12 is in byte[0] (big endian, with the most significant digits in the leftmost byte).

As for the quantity described as BCD and decimal, I would need to see actual values to understand what they're talking about.

like image 160
hatchet - done with SOverflow Avatar answered Oct 03 '22 11:10

hatchet - done with SOverflow


Each byte is two decimal digits, one in each nibble. If you show the bytes as hex, you can read the number easily.

0x08 0x27 0x42 0x17 0x75 = 827,421,775

You can get the high and low nibbles like this:

int high = currentByte >> 4;
int low = currentByte & 0xF;

Convert each byte into number like this:

int number = 10 * high + low;

But remember that each byte is 100 times bigger then the next byte.

With quantity having 3 decimal places, just divide the final number by 1,000 to get the actual value.

like image 33
SpacedMonkey Avatar answered Oct 03 '22 10:10

SpacedMonkey


CORRECT code:

// assume byte[] bcds is input
int result = 0;
foreach(byte bcd in bcds) {
    result *= 100;
    result += (10 * (bcd >> 4));
    result += bcd & 0xf;
}

You can also create a custom extension to a byte[] by creating a public static class:

public static class BitConverterExtension
{
    public static UInt64 FromBCDToExtUInt64(this byte[] b, byte[] bcds, uint nBytes, uint startOf)
    {
        UInt64 result = 0;
        uint i = 0;

        for (i = 0; i < nBytes; i++)
        {
            result *= 100;
            result += (UInt64)(10 * (bcds[startOf + i] >> 4));
            result += (UInt64)(bcds[startOf + i] & 0xf);
        }

        return (result);

    }
}
like image 45
Tyrant Avatar answered Oct 03 '22 12:10

Tyrant


i wrote this code and works for me:

 public uint BCD5ToInt(byte [] bcd)
{
uint outInt=0;

   for (int i = 0; i < bcd.Length; i++)
   {
       int mul = (int)Math.Pow(10,(i*2));
       outInt += (uint) (((bcd[i] & 0xF)) * mul);
       mul = (int)Math.Pow(10, (i * 2) + 1);
       outInt +=(uint)( ((bcd[i] >> 4) ) * mul);
   }

   return outInt;
}

This is reverse code:

  // Convert an unsigned integer into 5 bytes of 
    public byte[] IntToBCD5(uint numericvalue, int bytesize = 5)
    {
        byte[] bcd = new byte[bytesize];
        for (int byteNo = 0; byteNo < bytesize; ++byteNo)
            bcd[byteNo] = 0;
        for (int digit = 0; digit < bytesize * 2; ++digit)
        {
            uint hexpart = numericvalue % 10;
            bcd[digit / 2] |= (byte)(hexpart << ((digit % 2) * 4));
            numericvalue /= 10;
        }
        return bcd;
    }

And this is test code:

 public void test()
    {
        uint firstInt = 987654321;
        var array = IntToBCD5(firstInt);
        var outInt = BCD5ToInt(array);
        MessageBox.Show(outInt.ToString());
    }
like image 35
da jowkar Avatar answered Oct 03 '22 10:10

da jowkar