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.
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.
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.
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);
}
}
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());
}
My response might be a little bit late for the question but here is how I solved the question:
1- First I needed to find the length of the number e.g: 3422 -> 4, 100 -> 3
public static class NumbersUtility
{
public static int FindNumberLength(int number)
{
return Convert.ToInt32( Math.Floor(Math.Log(number,10))+1);
}
public static int FindNumberDivisor(int number)
{
return Convert.ToInt32(Math.Pow(10, FindNumberLength(number)-1));
}
public static int[] FindNumberElements(int number)
{
int[] elements = new int[FindNumberLength(number)];
int divisor = FindNumberDivisor(number);
for (int i = 0; i < elements.Length; i++)
{
elements[i] = number/divisor;
number %= divisor;
divisor /= 10;
}
return elements;
}
}
After that I have split the number into an array which makes it easier to traverse and process the number. There is one caveat though, if the number is of odd length then a zero has to be added to the beginning of the array.
public static byte[] IntToBCD(int[] input, bool isLittleEndian = false)
{
byte[] outArr = new byte[Convert.ToInt32(Math.Ceiling((double) input.Length/2))];
//Handle the case of an odd number in which a zero should be added at the beginning
if (input.Length%2 != 0)
{
//Use a temp array to expand the old one, you can use lists or
//anyother datastructure if you wish to
int[] newInput = new int[input.Length+1];
Array.Copy(input,0,newInput,1,input.Length);
newInput[0] = 0;
input = newInput;
//Dispose the temp array
newInput = null;
}
for (int i = 0; i < outArr.Length; i++)
{
outArr[i]=(byte)(input[i*2]<<4);
outArr[i]|=(byte)(input[i*2+1]);
}
return outArr;
}
来源:https://stackoverflow.com/questions/11701043/c-sharp-byte-to-bcd-and-bcd-to-int