将字母数字 PIC X(02) 转换为 cobol 中的十六进制值 9(01) COMP-3

convert alphanumeric PIC X(02) to hex value 9(01) COMP-3 in cobol

我有字母数字值 = '86',其长度定义为 PIC x(02)。我需要将它转换成十六进制 x'86' 并且它的长度定义为 PIC 9(01) comp-3.

示例:

01 WS-ALPHANUMERIC PIC X(02) VALUE '86'.   
01 WS-HEX          PIC 9(01) COMP-3.

PROCEDURE DIVISION.

MOVE WS-ALPHANUMERIC   TO WS-HEX.

DISPLAY WS-HEX.

STOP RUN

我的假脱机中出现 x'FF'。但我期待 x'86'。

我无法访问 IBM 大型机来测试此代码。

当我 运行 在线 GnuCOBOL v2.2 编译器上的代码时,我坚持使用 ASCII 而不是 EBCDIC。

我已经发布了代码。这是你必须做的。

  • 确保最高字节为 8,最低字节为 6。您正在将 EBCDIC 值转换为整数值。值 A - F 十六进制将具有与值 0 - 9 不同的 EBCDIC 值。
  • 确保乘法和加法正确

这是代码。您必须修复它才能使用 EBCDIC。

IDENTIFICATION DIVISION.
PROGRAM-ID. CONVERSION.

DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-ALPHANUMERIC              PIC X(02) VALUE '86'.

01 WS-WORK-FIELDS.
   05 WS-INPUT.
      10 WS-ONE. 
         15 WS-TOP-BYTE         PIC 99 COMP.
      10 WS-TWO.
         15 WS-BOTTOM-BYTE      PIC 99 COMP.
   05 WS-ACCUMULATOR            PIC S9(4) COMP.
   05 FILLER                    REDEFINES WS-ACCUMULATOR.
      10 FILLER                 PIC X.
      10 WS-HEX                 PIC X.

PROCEDURE DIVISION.
0000-BEGIN.
    MOVE WS-ALPHANUMERIC TO WS-INPUT
    DISPLAY WS-INPUT

    COMPUTE WS-TOP-BYTE = WS-TOP-BYTE - 183
    COMPUTE WS-BOTTOM-BYTE = WS-BOTTOM-BYTE - 183

    IF WS-TOP-BYTE NOT LESS THAN 16
        COMPUTE WS-TOP-BYTE = WS-TOP-BYTE - 57
    END-IF   

    IF WS-BOTTOM-BYTE NOT LESS THAN 16
        COMPUTE WS-BOTTOM-BYTE = WS-BOTTOM-BYTE - 57
    END-IF  
    
    DISPLAY WS-TOP-BYTE
    DISPLAY WS-BOTTOM-BYTE

    MOVE WS-TOP-BYTE TO WS-ACCUMULATOR
    MULTIPLY 16 BY WS-ACUMULATOR
    ADD WS-BOTTOM-BYTE TO WS-ACCUMULATOR

    DISPLAY WS-ACCUMULATOR
    DISPLAY WS-HEX

    GOBACK.
  1. 为什么您的代码没有产生您期望的输出

这只是我的猜测,因为在我的电脑上它不能那样工作。

当你MOVEWS-ALPHANUMERICWS-HEX时,字符串'86'转化为十进制数86。

然而 WS-HEX 只有一个字节长,并且是 COMP-3 格式。这种格式只能存储一位十进制数字和符号。 我猜想在您的环境中,当您将比容量更大的数字移动到 COMP-3 时,它会采用它可以容纳的最大十六进制值:0xF。 在我的环境中,它只取数字 86 的数字 6。

因此,当您显示时,它会转换为用法显示,因此您有第一个 0xF 用于用法格式,然后 0xF 用于我猜的“溢出”。

在我的电脑上你只会得到一个 0xF6。

  1. 产生预期输出的解决方案

免责声明:我原本以为您的输入只会是小数,例如“87596”、“12”或“88”。此解决方案不适用于 'F1' 或 '99F' 等十六进制输入。我通过改进这个

在下面的第 3 项和第 4 项中构建了更完整的解决方案

如果您的系统是 64 位,我建议的解决方案最多可以在输入字符串中占用 16 位数字,因为它需要 4 位来存储十六进制数字。

因此,如果您想要更大的输入,则必须使用多个结果变量。

如果你只想把它放在一个字节中,你只需要将 Result 设为 PIC 9(1) 而不是 PIC 9(18)

IDENTIFICATION DIVISION.
PROGRAM-ID. CNVRSN.

ENVIRONMENT DIVISION.

DATA DIVISION.
WORKING-STORAGE SECTION.

      01  RawInput PIC X(02) VALUE '86'.

      01  FormattedInput PIC 9(16).
      01  FractionedInput REDEFINES FormattedInput
         05 Digit PIC 9 OCCURS 16.          
      01  Shifting PIC 9(18) COMP-5 VALUE 1. 
      01  I PIC 99 COMP-5.

      01  Result PIC 9(18) COMP-5 VALUE 0.
      01  DisplayResult REDEFINES Result PIC X(8).
     


PROCEDURE DIVISION.

       MOVE RawInput TO FormattedInput.
  
       PERFORM VARYING I FROM LENGTH OF FractionedInput
                    BY -1 UNTIL I < 1
  
        COMPUTE Result = Result + Digit(I)*Shifting
        MULTIPLY 16 BY Shifting
  
       END-PERFORM
  
       DISPLAY 'DisplayResult : ' DisplayResult
       .
END PROGRAM CNVRSN.

该代码通过第一步 MOVE RawInput to FormattedInput.

将字符串转换为多个 USAGE DISPLAY

我们使用的事实是,每个数字的格式与只有一个数字的数字 (PIC 9) 的格式相同。这允许我们用 REDEFINES of FomattedInput in FractionedInput

拆分数组元素中的数字

如您所见,我从末尾到开始遍历数组,因为最低有效字节在数组的末尾(内存中的最高地址),而不是开头(内存中的最低地址)。

然后我们将每个十六进制数字向左移动 2^4(一个半字节,即十六进制数字的大小),根据需要多次移动,从而将每个十六进制数字放在正确的位置。

  1. 接受完整十六进制输入范围(内存密集型)的解决方案

这是代码:

IDENTIFICATION DIVISION.
PROGRAM-ID. CNVRSN.

ENVIRONMENT DIVISION.

DATA DIVISION.
WORKING-STORAGE SECTION.

  01  RawInput PIC X(02) VALUE '86'.

  01  FormattedInput PIC X(16).
  01  FractionedInput REDEFINES FormattedInput
     05 Digit PIC X OCCURS 16.          
  01  I PIC 99 COMP-5.
  
  01  ConversionTableInitializer. 
    05 FILLER PIC X(192).
    05 TenToFifteen PIC X(06) VALUE X'0A0B0C0D0E0F'.
    05 FILLER PIC X(41).
    05 ZeroToNine PIC X(10) VALUE X'00010203040506070809'.

  01  ConversionTable Redefines ConversionTableInitializer. 
    05 DigitConverter PIC 99 COMP-5 OCCURS 249.

  01  Result PIC 9(18) COMP-5 VALUE 0.
  01  DisplayResult REDEFINES Result PIC X(8).
 

PROCEDURE DIVISION.
   MOVE RawInput TO FormattedInput.

   PERFORM VARYING I FROM 1 BY 1 
                     UNTIL I > LENGTH OF FractionedInput
                        OR Digit(I) = SPACE


    COMPUTE Result = Result*16 + DigitConverter(Digit(I))

   END-PERFORM

   DISPLAY 'DisplayResult : ' DisplayResult
   .
END PROGRAM CNVRSN.

此解决方案的想法是将每个字符 (0,1...,E,F) 转换为其十六进制值。为此,我们将它们的编码值用作字符串(例如 A 为 0xC1 = 0d193)作为数组的索引。

这非常浪费内存,因为我们分配了 249 个字节来存储仅 16 个半字节的信息。然而,访问数组元素是一个非常快速的操作:我们正在用内存使用换取 cpu 效率。 该解决方案的基本思想是散列table。这个解决方案只不过是一个非常原始的散列table,其中散列函数是身份函数(一个非常非常糟糕的散列函数)。

  1. 另一种接受完整十六进制输入范围的解决方案(CPU密集型)

免责声明:此解决方案由@Jim Castro 在评论中提出。

IDENTIFICATION DIVISION. 
PROGRAM-ID. CNVRSN.

ENVIRONMENT DIVISION.

DATA DIVISION.
WORKING-STORAGE SECTION.

  01  RawInput PIC X(02) VALUE '86'.

  01  FormattedInput PIC X(16).
  01  FractionedInput REDEFINES FormattedInput
     05 Digit PIC 9 OCCURS 16.          
  01  ConversionString PIC X(16) VALUE '0123456789ABCDEF'.
  01  ConversionTable REDEFINES ConversionString.
     05 ConversionEntry OCCURS 16 INDEXED BY Idx.                          
       10 HexDigit PIC X.   

  01  I PIC 99 COMP-5.
  


  01  Result PIC 9(18) COMP-5 VALUE 0.
  01  DisplayResult REDEFINES Result PIC X(8).
 


PROCEDURE DIVISION.

   MOVE RawInput TO FormattedInput.

   PERFORM VARYING I FROM 1 BY 1 
                     UNTIL I > LENGTH OF FractionedInput
                        OR Digit(I) = SPACE


    SET Idx To 1
    SEARCH ConversionEntry
      WHEN HexDigit(Idx) = Digit(I)
         COMPUTE Result = Result*16 + Idx - 1            
    END-SEARCH
    

   END-PERFORM

   DISPLAY 'DisplayResult : ' DisplayResult
   .
 END PROGRAM CNVRSN.

这里的思路还是将字符串数字转换成它的值。然而,我们不是为了 cpu 效率而牺牲内存效率,而是相反。

我们有一个 ConversionTable,其中每个字符串都位于传达它们应该传达的值的索引处 + 1(因为在 COBOL 中数组是基于 0 的)。我们只需要找到匹配的字符,然后匹配字符的索引等于十六进制的值。

  1. 结论

有多种方法可以满足您的需求。基本思想是:

  1. 实现一种将字符转换为其十六进制值的方法
  2. 遍历输入字符串的所有字符,并使用它们的位置赋予它们正确的权重。

您的解决方案将始终是内存效率和时间效率之间的权衡。有时你想保留你的记忆,有时你希望执行速度非常快。有时你想找到一个中间立场。

为了朝这个方向前进,我们可以以牺牲 cpu 为代价,在内存方面改进第 3 项的解决方案。这将是第 3 项和第 4 项之间的折衷。 为此,我们可以使用模运算来限制要存储的可能性的数量。这样做意味着实现一个真正的散列table.