将字母数字 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.
- 为什么您的代码没有产生您期望的输出
这只是我的猜测,因为在我的电脑上它不能那样工作。
当你MOVE
从WS-ALPHANUMERIC
到WS-HEX
时,字符串'86'转化为十进制数86。
然而 WS-HEX 只有一个字节长,并且是 COMP-3 格式。这种格式只能存储一位十进制数字和符号。
我猜想在您的环境中,当您将比容量更大的数字移动到 COMP-3 时,它会采用它可以容纳的最大十六进制值:0xF。
在我的环境中,它只取数字 86 的数字 6。
因此,当您显示时,它会转换为用法显示,因此您有第一个 0xF 用于用法格式,然后 0xF 用于我猜的“溢出”。
在我的电脑上你只会得到一个 0xF6。
- 产生预期输出的解决方案
免责声明:我原本以为您的输入只会是小数,例如“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(一个半字节,即十六进制数字的大小),根据需要多次移动,从而将每个十六进制数字放在正确的位置。
- 接受完整十六进制输入范围(内存密集型)的解决方案
这是代码:
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,其中散列函数是身份函数(一个非常非常糟糕的散列函数)。
- 另一种接受完整十六进制输入范围的解决方案(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 的)。我们只需要找到匹配的字符,然后匹配字符的索引等于十六进制的值。
- 结论
有多种方法可以满足您的需求。基本思想是:
- 实现一种将字符转换为其十六进制值的方法
- 遍历输入字符串的所有字符,并使用它们的位置赋予它们正确的权重。
您的解决方案将始终是内存效率和时间效率之间的权衡。有时你想保留你的记忆,有时你希望执行速度非常快。有时你想找到一个中间立场。
为了朝这个方向前进,我们可以以牺牲 cpu 为代价,在内存方面改进第 3 项的解决方案。这将是第 3 项和第 4 项之间的折衷。
为此,我们可以使用模运算来限制要存储的可能性的数量。这样做意味着实现一个真正的散列table.
我有字母数字值 = '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.
- 为什么您的代码没有产生您期望的输出
这只是我的猜测,因为在我的电脑上它不能那样工作。
当你MOVE
从WS-ALPHANUMERIC
到WS-HEX
时,字符串'86'转化为十进制数86。
然而 WS-HEX 只有一个字节长,并且是 COMP-3 格式。这种格式只能存储一位十进制数字和符号。 我猜想在您的环境中,当您将比容量更大的数字移动到 COMP-3 时,它会采用它可以容纳的最大十六进制值:0xF。 在我的环境中,它只取数字 86 的数字 6。
因此,当您显示时,它会转换为用法显示,因此您有第一个 0xF 用于用法格式,然后 0xF 用于我猜的“溢出”。
在我的电脑上你只会得到一个 0xF6。
- 产生预期输出的解决方案
免责声明:我原本以为您的输入只会是小数,例如“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(一个半字节,即十六进制数字的大小),根据需要多次移动,从而将每个十六进制数字放在正确的位置。
- 接受完整十六进制输入范围(内存密集型)的解决方案
这是代码:
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,其中散列函数是身份函数(一个非常非常糟糕的散列函数)。
- 另一种接受完整十六进制输入范围的解决方案(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 的)。我们只需要找到匹配的字符,然后匹配字符的索引等于十六进制的值。
- 结论
有多种方法可以满足您的需求。基本思想是:
- 实现一种将字符转换为其十六进制值的方法
- 遍历输入字符串的所有字符,并使用它们的位置赋予它们正确的权重。
您的解决方案将始终是内存效率和时间效率之间的权衡。有时你想保留你的记忆,有时你希望执行速度非常快。有时你想找到一个中间立场。
为了朝这个方向前进,我们可以以牺牲 cpu 为代价,在内存方面改进第 3 项的解决方案。这将是第 3 项和第 4 项之间的折衷。 为此,我们可以使用模运算来限制要存储的可能性的数量。这样做意味着实现一个真正的散列table.