お問い合わせ

ホーム > COBOLのはなし > COBOLの10進数データについて

COBOLの10進数データについて

飯島 裕一(株式会社 SIT11)

はじめに

「外部10進数や内部10進数はCOBOLだけで使われる特殊で複雑なデータ属性だ」と 思っている方はいませんか?

これは、10進数が、そもそもCOBOL以外の言語ではほとんど見かけないということと、 10進数は理解ができるものの「外部」「内部」などのなじみない言葉が独特なデータ 表現形式を想像してしまい、「理解や使用が難しいのではないか」という思い込みから 生じているのではないかと思います。

実は、10進数は、人間が理解しやすい形で取り扱うデータ型なのです。
以下順に説明していきます。

外部10進数(*1)

まずは直感的にわかりやすい、外部10進数から説明していきます。「外部」という 言葉が付いていますが、通常の10進数をイメージしてください。

たとえば、5桁の数字を扱うデータの場合、外部10進数は PIC 9(5) あるいは PIC 99999 という表現で定義します(*2)。 そして各桁が1バイトの領域を使います。つまり、PIC 9(5) で定義された数字データ場合、 5バイトのメモリ領域を使用することになります。 また、表現できる数値の範囲は、0〜99999となります。

さて、それぞれのバイト内にはどのような値が格納されるのでしょうか?

実は、0から9の数字に対応して、それぞれ'0' から '9' までの文字が格納されます。 文字コードで表現すると、WindowsやUNIXではASCIIコード系なので0x30〜0x39 (*3)が、メインフレームであればEBCDICコード系 なので、0xF0 〜0xF9 が格納されます。

具体的に例を挙げると、

     01 A PIC 9(5) VALUE 123.

という定義がある場合、Windows/UNIXではデータ名A の値は 0x3030313233 として メモリ上に表現されます。

外部10進数の便利さは、ファイルやメモリのダンプを取った際に、その数値が直接 文字として表示されることです。これにより、数値を直観的に理解し、デバッグを 容易にすることができます。

(*1) ゾーン10進数、アンパック10進数とも呼ばれます。
(*2) PICはPICTUREの短縮形です。9(5)や99999をPICTURE文字列 といいます。
(*3) 0xNNはNNが16進数であることを示します。

符号付き外部10進数

符号付き数字を扱う際は、PICTURE文字列の先頭に'S'を付けます。
たとえば、-99999から+99999までの範囲の数字は、PIC S9(5)やPIC S99999で定義します。 ここで、Sは符号(正または負)を表します。

ここで疑問が出てきます。「はて符号はどうやって表現するのだろうか?」と。

少し考えていただくとわかりますが、符号だけで1バイトの領域を使うのは得策では ないですよね。なんとか、符号まで含めて5バイトにして、しかも文字のまま扱いたい ですよね。そこで、考えられたのが数字の一番右(つまり1の位)の数字に対応する 文字を正負によって変えるという表現方式でした。これがいわゆる重ね符号という ものです(*4)

メインフレーム時代に考えられた方式なので、EBCDICコード系で説明したほうが わかりやすいでしょう。
具体的には、1の位の数字’0’〜’9’を、正の場合は、’A’から’I’の文字で、 負の場合は、’J’から’R’の文字で表現します。コード値で表現すると、1の位の 数字0xF0〜F9 を、 正の場合は、0xC0〜C9で、負の場合は、0xD0〜D9で表現することに なります。

        符号なし    0   1   2   3   4   5   6   7   8   9
        文字       '0' '1' '2' '3' '4' '5' '6' '7' '8' '9'
        16進数     F0  F1  F2  F3  F4  F5  F6  F7  F8  F9

        正の場合   +0  +1  +2  +3  +4  +5  +6  +7  +8  +9
        文字       '{' 'A' 'B' 'C' 'D' 'E' 'F' 'G' 'H' 'I'
        16進数     C0  C1  C2  C3  C4  C5  C6  C7  C8  C9

        負の場合   -0  -1  -2  -3  -4  -5  -6  -7  -8  -9
        文字       '}' 'J' 'K' 'L' 'M' 'N' 'O' 'P' 'Q' 'R'
        16進数     D0  D1  D2  D3  D4  D5  D6  D7  D8  D9

例えば、+12345 は、 ‘1234E’ と表現され、-12345は、’1234N’と表現される ことになります。
ここで、+0と-0が出てきて少し戸惑うかもしれませんが、これは例えば、 +100は、 ‘10{‘ -100は、’10}’と表現するという意味です。

(*4) 符号は重ねずに独立した領域をもたせることも可能です(SIGN IS SEPARATE)。 また、符号部の位置を右端(最下位桁)ではなく左端(最上位桁)に持たせることも可能です(SIGN IS LEADING)。

内部10進数(*5)

続いて内部10進数について見ていきたいと思います。

COBOLが誕生した1959年当時、コンピュータの性能は現代に比べて格段に低く、 利用可能なメモリも限られていました。これらの技術的制約の中で、より多くの データを効率的に扱うために内部10進数が登場しました。

では、内部10進数はどのようにしてメモリ節約に貢献したのでしょうか?
実は、0から9までの数字は、二進数で表すと4ビット(0b0000から0b1001) (*6)で十分です。この表現方法は BCD(Binary-coded decimal、二進化十進数表現)と呼ばれます。内部10進数では、 このBCD表現を用いて各桁を4ビットで表現し、最下位(1の位の右側)に更に 符号領域4ビットを加えることで、数字を定義しました。

この方法により、内部10進数で表現される数値は外部10進数に比べてメモリ使用量が 約半分になります。例えば、外部10進数で5バイトを使用する数値は、内部10進数では 約2.5バイト(正確にはさらに符号部の4ビットが必要)で表現可能です。

しかし、内部10進数表現は外部10進数と異なり、数値が文字として表現されないため、 ダンプ中の内部10進数の数値を直感的に理解するのは難しいかもしれません。それでも、 メモリ効率の観点から見ると、内部10進数の導入は当時の技術的な制約を克服する重要な ステップでした。

(*5) パック10進数とも呼ばれます。
(*6) 0bNNNNは、NNNNが2進数であることを示します。

内部10進数の具体的表現

内部10進数では、符号がある場合でもない場合でも、最下位に4ビットの符号領域が 確保されます。符号なしの場合、この領域は0xF(固定)となります。一方、符号あり の場合、正の値には0xC、負の値には0xDが設定されます。

具体的な数値で見ていきましょう。
内部10進数は、外部10進数と同じくPICTURE句で数字の桁数や符号の有無を指定し、 さらにUSAGE PACKED-DECIMALあるいはCOMP-3(COMPUTATIONAL-3)を指定して、内部10進数 であることを明示します。
たとえば、5桁の符号なし内部10進数を定義する場合、以下のようになります。

     01 A PIC 9(5)  COMP-3.

ここでデータ名Aのメモリサイズは、5桁の数字が5×4ビット=20ビット分と、符号領域の 4ビット分で合わせて24ビット、つまり3バイトになります。例えば、「12345」という 値は、メモリ上に0x12345Fとして格納されます。

符号付きの内部10進数の場合、定義は以下のようになります。

     01 B PIC S9(5)  COMP-3.

データ名Bのメモリサイズはデータ名Aと同じく3バイトです。ただし、数値の表現が異なり、 「12345」はメモリ上に0x12345Cとして格納され、一方「-12345」は0x12345Dとして 格納されます。

10進数の重要性と小数点付き10進数の扱い

10進数は「人間にとって理解しやすい」という理由だけでなく、コンピュータの中で 誤差を最小限に抑えるためにも重要です。コンピュータは基本的に2進数を使用して 数値を表現しますが、2進数では0.1のような一部の10進数小数を正確に表現できません。 これは、2進数で表せる小数が1/2, 1/4, 1/8のような2nの表現形式に限られる ためです。

ビジネス領域で金銭の計算を行う際、このような誤差は許容できません。そのため、 10進数での計算がサポートされる必要があったのです。

COBOLでは、小数点付きの10進数も扱うことができます。整数部がn桁、小数部がm桁の 数値は、PIC 9(n)V9(m)として定義されます。Vは小数点位置を示しますが、実際の領域 としては確保されません。例えば、以下のように定義された場合、

     01 C  PIC  S9(2)V9(3) VALUE  12.345

データ名Cはメモリ上で5バイトとして領域が確保され、0x31323334C5という値が格納 されます(ここで0xC5は重ね符号です)。このように、COBOLでは小数部の有無に関 わらず、数値は内部表現上同じ方法で扱われ、小数を持つ10進数同士の演算はまず 小数点位置で桁合わせが行われた後に計算が実行されます。

最後に

COBOLは、その誕生以来、ビジネスアプリケーションにおける精密な数値処理の要求に 応え続けてきました。この言語の核となるのは、人間にとって直感的で、かつ計算上の誤差 を最小限に抑えることができる10進数のデータ表現です。外部10進数はその明瞭さと デバッグの容易さで、内部10進数はメモリ効率の良さで、それぞれが重要な役割を 果たしています。符号の扱いや小数点の表現においても、COBOLは柔軟でありながら正確な 計算を可能にしてきました。