Reputation: 48
I am trying to convert float to packed decimal but the closest solution i found is by Gilbert Le Blanc from this post : Java: Convert String to packed decimal
This solution was copied and pasted for hundreds of times all over the stack overflow, but it is deeply flawed :
public class PackedDecimal {
public static byte[] format(long number, int bytes) {
byte[] b = new byte[bytes];
final byte minusSign = 0x0D; // Minus
final byte noSign = 0x0F; // Unsigned
String s = Long.toString(number);
int length = s.length();
boolean isNegative = false;
if (s.charAt(0) == '-') {
isNegative = true;
s = s.substring(1);
length--;
}
int extraBytes = length - bytes + 1;
if (extraBytes < 0) {
// Pad extra byte positions with zero
for (int i = 0; i < -extraBytes; i++) {
b[i] = 0x00;
}
} else if (extraBytes > 0) {
// Truncate the high order digits of the number to fit
s = s.substring(extraBytes);
length -= extraBytes;
extraBytes = 0;
}
// Translate the string digits into bytes
for (int i = 0; i < length; i++) {
String digit = s.substring(i, i + 1);
b[i - extraBytes] = Byte.valueOf(digit);
}
// Add the sign byte
if (isNegative) {
b[bytes - 1] = minusSign;
} else {
b[bytes - 1] = noSign;
}
return b;
}
public static void main(String[] args) {
long number = -456L;
byte[] b = PackedDecimal.format(number, 5);
System.out.println("Number: " + number + ", packed: " + byteToString(b));
number = 0L;
b = PackedDecimal.format(number, 5);
System.out.println("Number: " + number + ", packed: " + byteToString(b));
number = 5823L;
b = PackedDecimal.format(number, 5);
System.out.println("Number: " + number + ", packed: " + byteToString(b));
number = 123456L;
b = PackedDecimal.format(number, 5);
System.out.println("Number: " + number + ", packed: " + byteToString(b));
}
public static String byteToString(byte[] b) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < b.length; i++) {
sb.append("0x");
sb.append(Integer.toHexString((int) b[i]).toUpperCase());
sb.append(" ");
}
return sb.toString();
}
}
And here are the test results.
Number: -456, packed: 0x0 0x4 0x5 0x6 0xD
Number: 0, packed: 0x0 0x0 0x0 0x0 0xF
Number: 5823, packed: 0x5 0x8 0x2 0x3 0xF
Number: 123456, packed: 0x3 0x4 0x5 0x6 0xF
The problem with this code is :
The number of half bytes used in packed decimal is calculated as length + 1 + (1 if the total number of half bytes is odd)
This code ignores all of that.
Here are some examples of packed decimal conversion (l=length, half of bytes used)(p=number of half of bytes used after decimal point)
:
(l=4)(p=0) +123 > 00123C (l=4)(p=0)-123 > 00123D
(l=4)(p=0)(Unsigned) 12345 > 12345F
(l=6)(p=2)(Unsigned) 12345.67 > 1234567F
(l=8)(p=2)(Unsigned) 12345.67 > 001234567F
(l=6)(p=3)(Unsigned) > 12345.67 > 2345670F
Any suggestions on how to improve it?
Upvotes: 0
Views: 6134
Reputation: 473
There is an option to pack numbers to COMP3 (COBOL) format using bit manipulation. Below code snippet will work for mainframe COMP3 format packing only
Each number is stored in latest system with 8 bits but COMP3 holds 2 digit in it. For example,
1) 123 - will be stored in 2 bytes - 123 will be treated as +123 - So stored in 2 bytes
2) 1234 - will be stored in 3 bytes - 123 will be treated as +1234 - So stored in 3 bytes, left most 4 bits will be ZEROs
3) -123 - will be stored in 2 bytes - So stored in 2 bytes
byte[] packed = new byte[(data.length() - startAt) / 2 + 1];
int inIdx = chars.length - 1;
int outIdx = packed.length - 1;
int temp = (chars[(inIdx--)] & 0xF) << 4;
digit |= temp;
packed[(outIdx--)] = (byte)(digit & 0xFF);
for (; outIdx >= 0; --outIdx) {
if (inIdx < 0)
break;
if ((chars[inIdx] < 48) || (chars[inIdx] > 57))
{
logger.createErr("Invalid numeric character at " + inIdx);
return null;
}
digit = chars[(inIdx--)] & 0xF;
if (inIdx >= 0) {
temp = (chars[(inIdx--)] & 0xF) << 4;
digit |= temp;
}
packed[outIdx] = (byte)digit;
}
return packed;
Upvotes: 0
Reputation: 2385
There are libraries for this. E.g. you could use JTOpen from IBM for AS400 compatibility. Its usage for converting a Java BigDecimal to a packed decimal and back is described in a blog post by Michael Wan - quote:
/**
* Java BigDecimal to Packed Decimal
*/
//15 means total number of digits, 5 means number of decimal places
AS400PackedDecimal packedDecimal = new AS400PackedDecimal(15, 5);
BigDecimal javaBigDecimal = new BigDecimal("1000.12345");
byte[] convertedBytesArray = packedDecimal.toBytes(javaBigDecimal);
/**
* Packed Decimal to Java Big Decimal
*/
BigDecimal convertedBigDecimal = (BigDecimal)packedDecimal.toObject(convertedBytesArray);
Other libraries which I do not know:
Upvotes: 1
Reputation: 1347
I googled a bit and found this: https://docs.oracle.com/cd/E18150_01/javadocs/CICS/com/stc/eways/cics/PackedDecimal.html
The class PackedDecimal
does have a method toPackedDecimal
which takes a Number and thus takes a Float
.
Upvotes: 0