Devin.KR

COBOL · 기본

데이터 처리로 배우는 COBOL

데이터 정의 - PIC 로 자릿수를 정한다

WORKING-STORAGE SECTION, 레벨 번호(01·05·77), PIC 9·X·A·S·V, VALUE, 그룹 항목과 기본 항목, 고정 소수점이 돈 계산에 맞는 이유

개발자KR · 원고 갱신

이 장에서 배우는 것

COBOL 프로그램은 먼저 데이터를 어떤 모양으로 담을지 정하고, 그다음에 처리를 적는다. 앞 장에서 네 개의 DIVISION 을 살펴보았고, 이 장에서는 그중 DATA DIVISION 의 WORKING-STORAGE SECTION 을 채운다. 신용협동조합 회원 계좌 한 건을 담을 그릇을 만들고, 각 칸의 종류와 자릿수를 PIC 로 정한다. 돈을 다루는 프로그램이 왜 소수점 위치를 선언해 두고 계산하는지도 함께 확인한다.

  • WORKING-STORAGE SECTION 이 무엇이고 변수를 어떻게 선언하는지 설명할 수 있다.
  • 레벨 번호 01, 05, 77 의 차이를 알고 그룹 항목과 기본 항목을 구분한다.
  • PIC 의 9, X, A, S, V 와 반복 표기 (n) 으로 필드의 종류와 자릿수를 정한다.
  • VALUE 절로 프로그램이 시작될 때의 초깃값을 지정한다.
  • 고정 소수점이 금액 계산에 맞는 이유를 이진 부동소수점과 비교해 말할 수 있다.

문제 상황

조합의 전산 담당자가 일일 거래 처리 프로그램을 만들기 시작한다고 하자. 입력으로 들어올 회원 계좌에는 계좌번호, 계좌 종류, 예금주 이름, 지점 코드, 잔액, 이율이 있다. 이 값들을 담을 자리가 없으면 프로그램은 아무것도 읽거나 계산할 수 없다.

자리를 만들 때 두 가지 실수가 흔하다. 하나는 자릿수를 넉넉히 잡지 않아 큰 금액이 잘리는 것이다. 다른 하나는 잔액을 소수 계산에 흔히 쓰는 방식으로 다루다가 1원 미만의 오차가 쌓이는 것이다. 하루에 수천 건을 처리하는 배치에서 오차가 한 건씩 쌓이면 마감 합계가 장부와 맞지 않는다. COBOL 은 이 문제를 줄이려고 필드를 선언할 때 자릿수와 소수점 위치를 직접 적게 한다.

WORKING-STORAGE SECTION 과 레벨 번호

WORKING-STORAGE SECTION 은 프로그램이 실행되는 동안 쓰는 변수를 선언하는 곳이다. 변수라는 말 대신 COBOL 에서는 데이터 항목(data item)이라고 부른다. 항목 하나는 이름, 레벨 번호, 모양(PIC), 필요하면 초깃값(VALUE)으로 이루어진다. 선언 한 문장은 마침표로 끝난다.

고정 형식에서 선언을 적는 위치

이 책의 코드는 고정 형식(fixed format)으로 쓴다. 1~6열은 일련번호 자리라 비워 두고, 7열은 표시 영역이다. 7열에 별표를 쓰면 그 줄은 주석이 된다. 8~11열이 A 영역, 12열부터 72열까지가 B 영역이다. 이 장에서는 01 과 77 레벨 번호를 A 영역(8열)에서 시작하고, 05 와 문장은 B 영역(12열)에서 시작한다. 72열을 넘는 글자는 컴파일러가 읽지 않으므로 긴 줄은 만들지 않는다.

그룹 항목과 기본 항목

레벨 번호는 항목들이 어떤 관계인지 나타내는 숫자다. 번호가 작은 항목이 큰 항목을 품는다. 하위 항목을 가진 항목을 그룹 항목(group item), 더 쪼개지지 않는 항목을 기본 항목(elementary item)이라 한다. 실제 값을 담는 것은 기본 항목이고 PIC 도 기본 항목에만 붙인다. 그룹 항목은 하위 항목을 이어 붙인 한 덩어리의 이름일 뿐이다.

이 장에서 쓰는 레벨 번호
레벨 번호쓰임PIC
01레코드 한 건처럼 독립된 최상위 항목, 그룹이거나 기본 항목기본 항목일 때만
0501 아래의 하위 항목, 02~49 중 하나를 쓸 수 있고 5씩 띄우는 것이 관례기본 항목일 때만
77다른 항목에 속하지 않는 독립 기본 항목, 하위 항목을 둘 수 없다반드시 있다

그룹 안에 그룹을 넣을 수도 있다. 다음은 개설일을 년·월·일로 나눈 예다. 05 항목이 그룹이 되고 그 아래 10 레벨이 기본 항목이 된다.

       01  WS-MEMBER.
           05  WS-OPEN-DATE.
               10  WS-OPEN-YEAR    PIC 9(4).
               10  WS-OPEN-MONTH   PIC 9(2).
               10  WS-OPEN-DAY     PIC 9(2).
           05  WS-ACCT-NO          PIC 9(6).

WS-OPEN-DATE 를 통째로 다루면 8자리 숫자 하나로 보이고, WS-OPEN-MONTH 를 다루면 월만 보인다. 같은 자리를 큰 단위와 작은 단위로 모두 부를 수 있다는 점이 그룹 항목의 쓸모다. 아래 그림은 이 장의 완성 코드에 나오는 계좌 그룹이 메모리에서 어떻게 이어 붙는지 보여 준다.

WS-ACCOUNT 는 여섯 기본 항목이 바이트 수대로 이어 붙은 33바이트 덩어리다

PIC 로 모양과 자릿수를 정한다

PIC 는 PICTURE 의 줄임말로, 항목에 어떤 문자를 몇 개 담을지 그림처럼 적는 절이다. 기호 하나가 한 자리를 뜻하고, 같은 기호가 이어지면 괄호 안 숫자로 줄여 쓴다. PIC 9(6) 은 PIC 999999 와 같다.

PIC 에 쓰는 기호와 뜻
기호뜻예담는 값
9숫자 한 자리PIC 9(6)100231
X영문자·숫자·기호 아무 문자 한 자리PIC X(10)KIM MINJUN
A영문자와 공백 한 자리PIC A(3)SEO
S부호를 가진다, 맨 앞에 한 번만 쓴다PIC S9(5)음수도 가능
V암묵적 소수점 위치, 자리를 차지하지 않는다PIC 9(3)V99소수 둘째 자리까지

숫자와 문자는 다른 종류다

PIC 9 항목은 숫자 항목이라 더하고 빼는 계산에 쓸 수 있다. PIC X 항목은 문자 항목이라 계산에 쓰지 않는다. 계좌번호는 숫자처럼 보이지만 더하거나 빼지 않으므로 계산 목적이 아니라 자릿수를 지키는 목적으로 PIC 9(6) 을 쓴다. 맨 앞의 0 도 값의 일부로 남는다. 이름은 문자이므로 PIC X 를 쓴다. PIC A 는 알파벳과 공백만 허용하는 더 좁은 문자 항목이고, 지점 코드 같은 세 글자 영문 약호에 어울린다. 실무에서는 대부분 PIC X 를 쓰므로 A 는 존재를 알아 두면 된다.

이 장의 예제는 DISPLAY 출력이 한 글자씩 결정되도록 영문 값만 쓴다. 한글처럼 한 글자가 여러 바이트를 차지하는 문자를 다루는 문제는 문자열을 다루는 장에서 따로 살핀다.

S 와 V

S 가 없는 숫자 항목은 부호가 없는 값, 곧 0 이상만 담는다. 계산 결과가 음수가 되어도 S 가 없으면 부호가 버려지고 크기만 남는다. 출금이 잔액보다 큰 상황에서 나오는 값을 잃지 않으려면 잔액처럼 마이너스가 될 수 있는 항목에 S 를 붙인다.

V 는 소수점이 있다고 치는 위치를 알려 준다. PIC S9(7)V99 는 정수 7자리, 소수 2자리, 부호를 가진 항목이며 숫자 9자리만 저장한다. 소수점 문자는 저장되지 않는다. 컴파일러가 V 위치를 기억해 두었다가 계산할 때 소수점을 맞춰 준다. 그래서 1250000.75 를 이 항목에 넣으면 저장된 것은 125000075 라는 아홉 자리이고, 그 어디에도 점은 없다. 기본 사용 방식에서는 숫자 한 자리가 한 바이트이고 부호는 마지막 자리 문자에 얹혀 저장되므로 이 항목은 9바이트다.

VALUE 로 초깃값을 준다

선언만 하고 값을 주지 않으면 그 항목에 무엇이 들어 있는지 믿을 수 없다. 처음부터 값이 정해져야 하는 항목에는 VALUE 절을 쓴다. 숫자 항목에는 숫자 리터럴(literal, 코드에 직접 적은 값)을, 문자 항목에는 따옴표로 묶은 문자열을 쓴다. 0 이나 공백은 ZERO, SPACES 같은 이름으로도 적을 수 있다. VALUE 의 종류가 PIC 와 맞지 않으면 컴파일러가 거부한다. VALUE 는 프로그램이 시작될 때 한 번 적용되고, 이후 실행 중에 값이 바뀌어도 다시 돌아오지 않는다.

고정 소수점이 돈 계산에 맞는 이유

컴퓨터가 흔히 쓰는 부동소수점은 수를 2진수로 바꿔 저장한다. 10진수 0.1 은 2진수로 바꾸면 끝나지 않는 소수가 되어 가장 가까운 값으로 잘라 저장한다. 그래서 0.1 을 열 번 더해도 정확히 1 이 되지 않는 경우가 생긴다. 이를테면 배정밀도(double precision)에서는 0.9999999999999999 가 나온다. 1원 미만 오차는 눈에 띄지 않지만 수만 건을 더하면 쌓이고, 두 값이 같은지 비교하는 조건에서 어긋난다.

COBOL 의 숫자 항목은 10진수 자릿수를 그대로 저장한다. PIC 9V9 에 0.1 을 넣으면 숫자 1 하나를 저장하고 소수점이 한 자리 앞에 있다고 기억할 뿐이다. 더하기는 정수 덧셈과 같은 방식으로 일어나서 열 번 더하면 숫자 10, 곧 1.0 이 된다. 자릿수가 선언되어 있으므로 몇 번째 소수 자리에서 자를지도 프로그램이 정한다. 그림으로 비교하면 다음과 같다.

0.1 을 열 번 더하면 고정 소수점은 정확히 1.0 이 되고 이진 부동소수점은 오차가 남는다

고정 소수점이 만능은 아니다. 선언한 자릿수를 넘는 값은 담을 수 없으므로 최대 금액을 미리 어림해서 정수 자리를 정해야 한다. 소수 자리를 넘는 계산 결과를 어떻게 줄일지는 계산을 다루는 다음 장에서 이어 간다. 이 장에서는 선언만으로 오차가 쌓일 이유가 줄어든다는 점만 확인한다.

완성 코드

회원 계좌 한 건을 WORKING-STORAGE 에 선언하고 값을 확인하는 프로그램이다. 입력 파일은 없고 표준 입력도 읽지 않는다. 부호와 소수점은 DISPLAY 의 출력 형식에 기대지 않고 IF 로 값을 비교해 확인한다. 금액을 읽기 좋게 찍는 방법은 출력 편집을 다루는 장에서 배운다. 파일 이름은 main.cob 로 저장한다.

main.cob

      * Chapter 2 - data definition for one member account
       IDENTIFICATION DIVISION.
       PROGRAM-ID. MAIN.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       77  WS-ADD-COUNT        PIC 9(2)      VALUE 0.
       77  WS-REC-LEN          PIC 9(3)      VALUE 0.
       77  WS-MEMO             PIC X(8)      VALUE "OK".
       77  WS-ADJUST           PIC S9(5)V99  VALUE -350.50.
       77  WS-TENTH            PIC 9V9       VALUE 0.1.
       77  WS-SUM              PIC 9V9       VALUE 0.

       01  WS-ACCOUNT.
           05  WS-ACCT-NO      PIC 9(6)      VALUE 100231.
           05  WS-ACCT-TYPE    PIC X         VALUE "S".
           05  WS-OWNER-NAME   PIC X(10)     VALUE "KIM MINJUN".
           05  WS-BRANCH-CODE  PIC A(3)      VALUE "SEO".
           05  WS-BALANCE      PIC S9(7)V99  VALUE 1250000.75.
           05  WS-RATE         PIC 9V9(3)    VALUE 0.025.

       PROCEDURE DIVISION.
       MAIN-PARA.
           MOVE FUNCTION LENGTH(WS-ACCOUNT) TO WS-REC-LEN
           DISPLAY "ACCOUNT NO : " WS-ACCT-NO
           DISPLAY "TYPE       : " WS-ACCT-TYPE
           DISPLAY "OWNER      : " WS-OWNER-NAME
           DISPLAY "BRANCH     : " WS-BRANCH-CODE
           DISPLAY "GROUP SIZE : " WS-REC-LEN
           DISPLAY "MEMO       : [" WS-MEMO "]"
           IF WS-BALANCE = 1250000.75
               DISPLAY "BALANCE MATCHES 1250000.75"
           END-IF
           IF WS-RATE = 0.025
               DISPLAY "RATE MATCHES 0.025"
           END-IF
           IF WS-ADJUST < 0
               DISPLAY "ADJUST IS NEGATIVE"
           END-IF
           PERFORM 10 TIMES
               ADD WS-TENTH TO WS-SUM
               ADD 1 TO WS-ADD-COUNT
           END-PERFORM
           DISPLAY "ADDITIONS " WS-ADD-COUNT
           IF WS-SUM = 1
               DISPLAY "TEN TIMES 0.1 IS EXACTLY 1.0"
           END-IF
           STOP RUN.

줄별 해설

첫 줄의 주석. 7열에 별표가 있어 컴파일러가 무시하는 줄이다. 코드 안의 설명은 영문으로 짧게 두었다.

IDENTIFICATION DIVISION 과 PROGRAM-ID. 프로그램 이름을 MAIN 으로 정한다. 앞 장에서 본 구조 그대로다.

WORKING-STORAGE SECTION. 이 줄 아래부터 PROCEDURE DIVISION 앞까지가 선언 영역이다.

77 레벨 여섯 줄. 서로 관계없는 독립 항목이다. WS-ADD-COUNT 는 덧셈 횟수를 세는 두 자리 숫자이고, WS-REC-LEN 은 그룹의 길이를 받는 세 자리 숫자다. WS-MEMO 는 여덟 칸 문자 항목에 두 글자만 넣어 남는 칸이 공백으로 채워지는 모습을 보기 위한 것이다. WS-ADJUST 는 S 가 붙은 음수 초깃값이고, WS-TENTH 와 WS-SUM 은 소수 한 자리를 가진 항목이다. WS-SUM 의 최댓값은 9.9 라서 합계 1.0 은 충분히 들어간다.

01 WS-ACCOUNT. PIC 가 없으므로 그룹 항목이다. 아래 05 레벨 여섯 개가 기본 항목이다.

WS-ACCT-NO 와 WS-ACCT-TYPE. 계좌번호는 숫자 6자리, 계좌 종류는 문자 한 자리다. PIC X 는 괄호가 없으면 한 자리다.

WS-OWNER-NAME 과 WS-BRANCH-CODE. 이름은 열 칸을 꼭 채우는 KIM MINJUN 이다. 공백도 한 칸으로 세어 열 글자가 된다. 지점 코드는 알파벳 세 칸이다.

WS-BALANCE 와 WS-RATE. 잔액은 부호 있는 정수 7자리와 소수 2자리, 이율은 부호 없는 정수 1자리와 소수 3자리다. 두 값 모두 VALUE 에 소수점이 있는 리터럴을 쓰고, 컴파일러가 V 위치에 맞추어 저장한다.

MOVE FUNCTION LENGTH(WS-ACCOUNT) TO WS-REC-LEN. 그룹이 차지하는 바이트 수를 구해 WS-REC-LEN 에 옮긴다. 6+1+10+3+9+4 로 33이다. 값 옮기기는 다음 장에서 자세히 다룬다.

DISPLAY 여섯 줄. 항목 값을 화면에 찍는다. 숫자 항목은 선언한 자릿수만큼 앞의 0 까지 찍힌다. WS-MEMO 는 대괄호로 감싸서 뒤쪽 공백 여섯 칸을 눈으로 확인한다.

IF 세 개. 잔액, 이율, 조정액을 리터럴과 비교한다. 저장된 값과 소수 자릿수가 정확히 맞으면 해당 메시지가 나온다. WS-ADJUST 는 S 가 있어서 0 보다 작다는 비교가 참이 된다. 조건문의 자세한 규칙은 조건을 다루는 장에서 배운다.

PERFORM 10 TIMES. 안의 두 문장을 열 번 되풀이한다. ADD WS-TENTH TO WS-SUM 으로 0.1 을 합계에 더하고, ADD 1 TO WS-ADD-COUNT 로 횟수를 센다. 반복 문장은 뒤에서 제대로 배우므로 여기서는 열 번 돈다고만 이해하면 충분하다.

마지막 IF 와 STOP RUN. 합계가 정확히 1 인지 비교하고 프로그램을 끝낸다.

실행 결과

$ cobc -x -Wall main.cob
$ ./main
ACCOUNT NO : 100231
TYPE       : S
OWNER      : KIM MINJUN
BRANCH     : SEO
GROUP SIZE : 033
MEMO       : [OK      ]
BALANCE MATCHES 1250000.75
RATE MATCHES 0.025
ADJUST IS NEGATIVE
ADDITIONS 10
TEN TIMES 0.1 IS EXACTLY 1.0

실무에서 자주 틀리는 것

V 를 빼먹어 소수 자리가 사라진다

금액에 소수 자리가 있는데 정수 항목으로 선언하면 소수 부분이 잘려 나간다.

      * wrong: no decimal places
       05  WS-FEE      PIC 9(5).
           MOVE 123.45 TO WS-FEE
      * WS-FEE now holds 00123

      * fixed: two decimal places declared
       05  WS-FEE      PIC 9(5)V99.
           MOVE 123.45 TO WS-FEE

오류 메시지가 나오지 않으므로 합계가 몇 원씩 모자란 뒤에야 발견되기 쉽다. 값이 정수가 아니라면 선언부터 소수 자리를 잡는다.

자릿수를 빠듯하게 잡는다

지금 데이터의 최댓값에 맞춰 자릿수를 잡으면, 훗날 더 큰 값이 들어올 때 윗자리가 잘린 값이 저장될 수 있다.

      * wrong: capacity 99,999.99
       05  WS-BALANCE  PIC S9(5)V99.

      * fixed: room for 9,999,999.99
       05  WS-BALANCE  PIC S9(7)V99.

누적 합계처럼 값이 커지기만 하는 항목은 개별 값보다 훨씬 넉넉하게 정한다. 넘칠 때의 처리는 계산을 다루는 장에서 이어 간다.

음수가 될 수 있는 항목에 S 를 빼먹는다

S 가 없으면 부호를 저장할 자리가 없어서 음수 결과가 크기만 남는다.

      * wrong: unsigned, a negative result loses its sign
       05  WS-BALANCE  PIC 9(7)V99.

      * fixed: signed
       05  WS-BALANCE  PIC S9(7)V99.

마이너스 잔액이 0 이상으로 보이면 조건문이 틀린 길로 가므로, 증감이 있는 금액 항목은 S 를 붙이는 습관을 들인다.

그룹 항목에 PIC 를 붙이거나 77 아래에 하위 항목을 둔다

PIC 는 기본 항목에만 붙는다. 하위 항목이 있는 줄에 PIC 를 쓰면 컴파일러가 거부한다.

      * wrong: group item with PIC
       01  WS-ACCOUNT      PIC X(33).
           05  WS-ACCT-NO  PIC 9(6).

      * fixed: the group has no PIC
       01  WS-ACCOUNT.
           05  WS-ACCT-NO  PIC 9(6).

77 레벨 항목도 하위 항목을 둘 수 없다. 쪼개 쓸 가능성이 있다면 처음부터 01 로 선언한다.

한눈에 보기

이 장에서 배운 선언 요소 정리
요소역할예
WORKING-STORAGE SECTION실행 중 쓰는 항목을 선언하는 곳DATA DIVISION 안
01 / 05 / 77최상위·하위·독립 항목의 레벨 번호01 WS-ACCOUNT.
그룹 항목하위 항목을 품는 이름, PIC 없음WS-ACCOUNT
기본 항목값을 담는 항목, PIC 가 있음WS-ACCT-NO
9 · X · A숫자, 문자, 알파벳 한 자리PIC 9(6)
S · V부호, 암묵적 소수점 위치PIC S9(7)V99
VALUE프로그램 시작 때의 초깃값VALUE 0.025

연습 문제

  1. 다음 두 항목이 각각 몇 바이트인지 쓰시오. (1) PIC S9(5)V99 (2) PIC X(8)
  2. 1회 거래 한도 금액을 담는 항목을 선언하시오. 최댓값은 99,999,999원이고 원 단위 이하는 없으며 음수가 될 일이 없다. 이름은 WS-LIMIT 로, 초깃값은 5,000,000 으로 하고 77 레벨로 쓴다.
  3. 다음 선언의 문제를 찾아 고치시오.
           01  WS-MEMBER       PIC X(20).
               05  WS-NAME     PIC X(12).
               05  WS-GRADE    PIC 9.
  4. 예금 이율은 연 3.75% 형태로 소수 둘째 자리까지 쓰고 100 미만이다. 이를 소수 표기 그대로(예: 3.75) 담는 PIC 를 쓰고 이름과 VALUE 3.75 를 포함해 05 레벨로 선언하시오.

정답과 해설

  1. (1) 9바이트다. 숫자 자리가 정수 5와 소수 2로 모두 7자리가 아니라 2자리가 아닌 7개이므로 7바이트라고 읽기 쉽지만, 5+2=7 자리이므로 7바이트다. S 는 별도 바이트가 아니라 마지막 자리에 얹히고 V 는 자리를 차지하지 않는다. (2) 8바이트다. 문자 한 자리가 한 바이트다. 따라서 정답은 (1) 7바이트, (2) 8바이트다.
  2.        77  WS-LIMIT        PIC 9(8)      VALUE 5000000.
    정수 8자리면 99,999,999 까지 들어가고, 소수 자리가 없으니 V 는 필요 없으며, 음수가 없으니 S 도 붙이지 않는다.
  3. 그룹 항목인 01 WS-MEMBER 에 PIC 가 붙어 있어 컴파일러가 거부한다. PIC X(20) 을 지우고 마침표로 끝내면 된다.
           01  WS-MEMBER.
               05  WS-NAME     PIC X(12).
               05  WS-GRADE    PIC 9.
  4.            05  WS-DEPOSIT-RATE PIC 99V99     VALUE 3.75.
    100 미만이므로 정수 2자리, 소수 둘째 자리까지이므로 V99 이고 음수가 아니므로 S 는 없다. 총 4바이트다.
오탈자·오류 제보 비공개로 접수되어 원고 수정에 반영됩니다

이메일 등 개인정보는 받지 않습니다. 답변이 필요한 질문은 아래 댓글을 이용해 주세요.

READER FEEDBACK

질문·의견

내용에 관한 질문이나 더 나은 설명을 위한 의견을 남겨 주세요. 오탈자는 위의 제보 양식이 더 빨리 반영됩니다. 이 댓글은 원래 게시글과 같은 자리에 쌓입니다.

댓글 0

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

댓글을 남기려면 로그인이 필요합니다.