alan
alan

Reputation: 48

How to convert data to packed decimal format

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 :

  1. it deals only with int's. double and big decimal can be converted to packed decimal too.
  2. each digit represented by half a byte, therefore the number of digits in packed decimal must be always even not odd.
  3. From what i learned, in COBOL run applications usually there is a length variable for each field of data that is converted, and the length of number of digits after the decimal point. The code has a fixed length of 5 for each packed decimal number.

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

Answers (3)

Krishna
Krishna

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

Bernhard Stadler
Bernhard Stadler

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

arnonuem
arnonuem

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

Related Questions