Как преобразовать данные в упакованный десятичный формат - PullRequest
0 голосов
/ 11 июня 2019

Я пытаюсь преобразовать число с плавающей запятой в упакованное десятичное число, но самое близкое решение, которое я нашел, это Гилберт Ле Бланк из этого поста: Java: преобразование строки в упакованное десятичное число

Это решение было скопированои сотни раз вставляется по всему переполнению стека, но оно глубоко испорчено:

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();
    }

}

И вот результаты теста.

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 

Проблема с этим кодом:

  1. он имеет дело только с int.двойные и большие десятичные числа также могут быть преобразованы в упакованные десятичные числа.
  2. каждая цифра представлена ​​половиной байта, поэтому число цифр в упакованном десятичном числе всегда должно быть даже не нечетным.
  3. Из того, что яузнал, что в приложениях, запускаемых на языке COBOL, обычно есть переменная длины для каждого поля данных, которое преобразуется, и длина числа цифр после десятичной точки.Код имеет фиксированную длину 5 для каждого упакованного десятичного числа.

Количество полубайтов, используемых в упакованном десятичном числе, рассчитывается как длина + 1 + (1, если общее количество полубайтов нечетное)

Этокод игнорирует все это.

Вот несколько примеров упакованного десятичного преобразования (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

Есть предложения о том, как его улучшить?

Ответы [ 3 ]

1 голос
/ 06 июля 2019

Для этого есть библиотеки.Например, вы можете использовать JTOpen от IBM для совместимости с AS400.Его использование для преобразования Java BigDecimal в упакованный десятичный код и обратно описано в сообщении в блоге Майкла Вана - цитата:

/**
 * 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);

Другие библиотеки, которые я не знаю:

0 голосов
/ 10 июля 2019

Существует возможность упаковки чисел в формат COMP3 (COBOL) с использованием битовых манипуляций. Ниже фрагмент кода будет работать только для упаковки мэйнфреймов формата COMP3

Каждый номер хранится в последней системе с 8 битами, но в COMP3 содержится 2 цифры. Например,

1) 123 - будет храниться в 2 байтах - 123 будет обрабатываться как +123 - поэтому хранится в 2 байтах

2) 1234 - будет храниться в 3 байтах - 123 будет обрабатываться как +1234 - Таким образом, хранится в 3 байтах, самые левые 4 бита будут нулями

3) -123 - будет храниться в 2 байтах - поэтому хранится в 2 байтах

    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;
0 голосов
/ 05 июля 2019

Я немного погуглил и нашел это: https://docs.oracle.com/cd/E18150_01/javadocs/CICS/com/stc/eways/cics/PackedDecimal.html

Класс PackedDecimal имеет метод toPackedDecimal, который принимает число и, следовательно, принимает Float.

...