Skip to content

Instantly share code, notes, and snippets.

@neuro-sys
Last active January 13, 2022 03:34
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save neuro-sys/953548 to your computer and use it in GitHub Desktop.
Save neuro-sys/953548 to your computer and use it in GitHub Desktop.
BCD Conversion in java
/*
* Copyright 2010 Firat Salgur
* Improved by Abdallah Abdelazim
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Convert between decimal & BCD (binary-coded decimal).
*/
public class BcdConverter {
/**
* Encodes a positive integer number into an unsigned packed BCD.
*
* @param num a positive integer number to encode (maximum value of 2<sup>63</sup>-1).
* @return BCD representation of the passed number argument.
* @throws IllegalArgumentException if the passed num argument is negative.
*/
public static byte[] decimalToBcd(long num) {
if (num < 0) throw new IllegalArgumentException(
"The method decimalToBcd doesn't support negative numbers." +
" Invalid argument: " + num);
int digits = 0;
long temp = num;
while (temp != 0) {
digits++;
temp /= 10;
}
int byteLen = digits % 2 == 0 ? digits / 2 : (digits + 1) / 2;
byte[] bcd = new byte[byteLen];
for (int i = 0; i < digits; i++) {
byte tmp = (byte) (num % 10);
if (i % 2 == 0) {
bcd[i / 2] = tmp;
} else {
bcd[i / 2] |= (byte) (tmp << 4);
}
num /= 10;
}
for (int i = 0; i < byteLen / 2; i++) {
byte tmp = bcd[i];
bcd[i] = bcd[byteLen - i - 1];
bcd[byteLen - i - 1] = tmp;
}
return bcd;
}
/**
* Decodes an unsigned packed BCD to its integer number.
*
* @param bcd the BCD to decode.
* @return the decoded integer number.
*/
public static long bcdToDecimal(byte[] bcd) {
return Long.parseLong(BcdConverter.bcdToString(bcd));
}
/**
* Decodes an unsigned packed BCD to its integer number wrapped in a {@code String}.
*
* @param bcd the BCD to decode.
* @return the decoded integer number wrapped inside a {@code String}.
*/
public static String bcdToString(byte[] bcd) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bcd.length; i++) {
sb.append(bcdToString(bcd[i]));
}
return sb.toString();
}
/**
* Decodes an unsigned packed BCD byte to its integer number wrapped in a {@code String}.
*
* @param bcd the BCD byte to decode.
* @return the decoded integer number wrapped inside a {@code String}.
*/
public static String bcdToString(byte bcd) {
StringBuilder sb = new StringBuilder();
byte high = (byte) (bcd & 0xf0);
high >>>= (byte) 4;
high = (byte) (high & 0x0f);
byte low = (byte) (bcd & 0x0f);
sb.append(high);
sb.append(low);
return sb.toString();
}
public static void main(String[] args) {
System.out.println("Testing decimalToBcd:");
System.out.println(String.format(" %10s %20s %18s", "Value", "Expected", "Result"));
BcdConverter.testForValue(1L, "00000001");
BcdConverter.testForValue(11L, "00010001");
BcdConverter.testForValue(111L, "0000000100010001");
BcdConverter.testForValue(1111L, "0001000100010001");
BcdConverter.testForValue(11111L, "000000010001000100010001");
BcdConverter.testForValue(42, "01000010");
BcdConverter.testForValue(112233L, "000100010010001000110011");
BcdConverter.testForValue(12345L, "000000010010001101000101");
System.out.println("\nTesting two way conversion using decimalToBcd and back using bcdToDecimal:");
System.out.println(String.format(" %10s %20s %18s", "Value", "Expected", "Result"));
BcdConverter.testForValue(1L);
BcdConverter.testForValue(11L);
BcdConverter.testForValue(111L);
BcdConverter.testForValue(1111L);
BcdConverter.testForValue(11111L);
BcdConverter.testForValue(12983283L);
BcdConverter.testForValue(9832098349L);
}
/* Testing Utilities */
private static void testForValue(long val, String expected) {
String binaryString = BcdConverter.byteArrayToBinaryString(BcdConverter.decimalToBcd(val));
System.out.print(String.format("Testing: %10d -> %30s %4s\n", val, binaryString
, binaryString.equals(expected) ? "[OK]" : "[FAIL]"));
}
private static void testForValue(long val) {
long newVal = BcdConverter.bcdToDecimal(BcdConverter.decimalToBcd(val));
System.out.print(String.format("Testing: %10d -> %30d %4s\n", val, newVal
, newVal == val ? "[OK]" : "[FAIL]"));
}
private static String byteArrayToBinaryString(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
String byteInBinary = String.format("%8s", Integer.toBinaryString(b)).replace(' ', '0');
sb.append(byteInBinary);
}
return sb.toString();
}
}
Testing DecimalToBCD:
Testing: 1 -> 00000001 [OK]
Testing: 11 -> 00010001 [OK]
Testing: 111 -> 0000000100010001 [OK]
Testing: 1111 -> 0001000100010001 [OK]
Testing: 11111 -> 000000010001000100010001 [OK]
Testing: 42 -> 01000010 [OK]
Testing: 112233 -> 000100010010001000110011 [OK]
Testing: 12345 -> 000000010010001101000101 [OK]
Testing two way conversion using DecimalToBCD and back using BCDToDecimal:
Testing: 1 -> 1 [OK]
Testing: 11 -> 11 [OK]
Testing: 111 -> 111 [OK]
Testing: 1111 -> 1111 [OK]
Testing: 11111 -> 11111 [OK]
Testing: 12983283 -> 12983283 [OK]
Testing: 9832098349 -> 9832098349 [OK]
@shariqislam786
Copy link

this is not working properly, if you don't use the built in function to convert to string using the returned array.

if you pass 255, 123 or any three digit to DecToBCDArray it gives a byte array of only two digits which should be three however if you pass the returned byte array to BDCtoString it regenerates the number successfully. So basically its useless if you want to use it with something else(which is usually the case).

@neuro-sys
Copy link
Author

@shariqislam786

Hi, any three or four digit number such as 123 or 1234 is a 2 byte value because each BCD digit is only a nibble (4-bits). If you're working with embedded devices such as a Point of sale device (POS), for which I had written this utility class, the device requires a continous stream of bytes in this format for BCD values. i.e. you'd more likely need to dump byte[] at your UART or whatever serial port than convert to String.

I included a main mehtod to test DecToBCDArray, and it outputs:

Testing:      12345 ->       000000010010001101000101 [OK]

In the wikipedia article for BCD there's an example as such:

 Decimal:          1     2    3     4    5
 Binary :  0000 0001  0010 0011  0100 0101

The test method I included asserts equally with that of the Wikipedia example.

You're right in that on occasion it might be more convenient to output a byte (8-bit value) for each digit in the given number instead of concatenating each BCD value into a byte, so that the caller can do this at will. I'll include such a method in the class file.

Thanks.

@simplyi
Copy link

simplyi commented Oct 18, 2017

When I use this code my end result starts with a leading zero. Is it correct? Does it need to start with a leading zero?

@Abdallah-Abdelazim
Copy link

@neuro-sys I've added enhancements over your code in this fork.
Specifically, I added proper JavaDoc explaining the methods, added input validation & reviewed the naming of your methods to match standard Java naming conventions. If you see they are good modifications, maybe consider editing your gist to include some or all of the modifications.

@neuro-sys
Copy link
Author

@Abdallah-Abdelazim
Thanks, I incorporated your changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment