01.S370における数と文字の表現

メインフレームに限らずコンピューターのCPUは、その内部で2進数を使用して演算処理を行っていることはよく知られています。アセンブラー言語のプログラミングでは、CPUの機械命令を直接使用してメモリーにも直接アクセスすることになります。COBOLやPL/Iなどのいわゆる高級言語と異なり、数や文字あるいはデータがCPUでどのように処理されてメモリーにどのように格納されるのかが具体的にわかっている必要があります。そのために、数や文字が内部でどのように表現されるかを理解することは大切です。

2進数と16進数

私たちは日常生活では10進数(decimal number)を使います。0から始まって1、2、3と続き7、8、9となったらそれ以上の数字がないので繰り上がって10となります。0~9までの10の数字で表現するため10進数と呼ばれます。
ところが、コンピューターのCPUは2進数(binary number)を使います。0から始まり1になると、次はそれ以上の数字がないので繰り上がって10となります。0と1の2つの数字で表現するため2進数と呼ばれます。0か1かの2つの状態でしかないので、電気信号のオンとオフに対応させてハードウェアとしての記憶素子に情報を蓄えることができます。
この0か1かの何れかの状態によって記憶素子に蓄えられる情報を「ビット(bit)」と呼びます。0の状態がビット・オフ、1の状態がビット・オンです。CPUのような機械にとっては0か1かの2進数の方が自然なわけです。なお、S/370アーキテクチャーでは10進数も扱えますが基本となる演算処理は内部で2進数に変換されて行われます。

CPUが2進数を使用しメモリーにもビットのON/OFFでデータが蓄えられるならば、例えば100と言う数値は2進数では1100100、379は101111011、1234は10011010010となりますが、とても人間が扱える表現方法ではありません。それでもアセンブラー・プログラミングでは数の取り扱いや演算結果、命令動作の確認などでCPUが行うとおりにやってみる必要が生じます。そこで、CPUにとっては自然だが人間にとってはやっかいな2進数を扱うために16進数(hexadecimal number)を用います。16進数は0から始まって1、2、3と続き7、8、9となったら次にA、B、C、D、E、Fと続きます。Fの次はそれ以上の数字がないので繰り上がって10となります。0~9とA~Fまでの16の数字で表現するため16進数と呼ばれます。AとかBは数字じゃなくて文字だろうと思うかも知れませんが、それは10進数に慣れていることとAやBはアルファベット文字という知識が先立つからです。16進数ではA、B、C、D、E、Fは数字の1部だと割り切ってください。

16進数は2進数と密接な関係を持ちます。16は2の4乗なので4ビットで表現できます。したがって、2進数を下位桁から4ビットずつ区切れば簡単に16進数に変換できます。先ほど例示した100と言う数値は2進数では1100100(0110 0100)なので16進数では64、379は2進数では101111011(0001 0111 1011)なので16進数では17B、1234は2進数では10011010010(0100 1101 0010)なので16進数では4D2となります。2進数と10進数間の変換は計算が必要ですが、2進数と16進数間は対応表で簡単に変換できます。実際にプログラミングを始めればいちいち対応表など見なくても直感で変換できるようになります。

    16進数、2進数、10進数対応表
    16進数字 対応する2進数 対応する10進数
    0 0000 0
    1 0001 1
    2 0010 2
    3 0011 3
    4 0100 4
    5 0101 5
    6 0110 6
    7 0111 7
    8 1000 8
    9 1001 9
    A 1010 10
    B 1011 11
    C 1100 12
    D 1101 13
    E 1110 14
    F 1111 15

ビット操作やマスク値の設定など、2進と16進の変換はアセンブラー・プログラミングでは常に必要となります。実際にはさほど長い桁数を変換することはなく、4桁の2進と1桁の16進、8桁の2進と2桁の16進をそれぞれ変換する程度です。なお、2進数と10進数の変換計算についてはそのような計算を行うプログラムでも作らない限り必要となることはありません。16進数と10進数も変換には計算が必要でデバッグ作業ではしばしば必要になりますが、n進数計算機能を持つ関数電卓(今はwebに無料の計算サイトがいくらでもあるしスマホアプリも豊富)を使えば簡単です。

バイトとワード

メモリーにはデータがビット単位に記憶されます。しかし、私たちは1ビット、2ビットと言った長さの数を扱うことはほとんどありません。コンピューターではメモリーを複数の記憶素子をまとめて扱い、CPUがメモリーをアクセスする際の単位とします。S/370アーキテクチャーでは8ビットを1つのグループにしてバイト(byte)を構成します。メモリーはバイト単位にアクセスされるため、メモリー・アドレスもバイト単位に振られます。なお、1byte=8bitと決まっているわけではありません。初期のコンピューターでは7ビットや9ビットなどで1バイトが構成されることもありました。S/360システムが1byte=8bitを採用したことや80年代以降Intel社の8bitや16bitのCPUが普及したので1byte=8bitが定着してしまいました。

S/370アーキテクチャーではバイトは4つにまとめられワード(語)を構成します。演算に使われる汎用レジスターも32bitで構成されています。さらに、半分の長さのハーフワード(半語)、倍の長さのダブルワード(倍語)が使われます。そのためCPU命令ではバイト、ハーフワード、ワード(フルワード)、ダブルワードのいずれかの単位でメモリーにアクセスすることができます。文字列をまとめて移動できる命令などもありますが、この場合でもバイト単位で合計○○○バイトのデータをメモリーに書き込むという動作になり、メモリーへの実際のアクセスはバイト単位に行われます。

    長さ ビット数
    バイト 1バイト 8ビット
    ハーフワード 2バイト 16ビット
    ワード 4バイト 32ビット
    ダブルワード 8バイト 64ビット

数の表現(2進整数)

S/370アーキテクチャーでは、整数は2進整数とも呼ばれ、符号を含み16、32あるいは64ビットで表現されます。メーカーによってはマニュアルに2進固定小数点(Fixed Binary)と載っているものもありますが、実際には整数部のみが使われますので単なる整数と考えてかまいません。呼ばれ方の違いであってIBM、富士通、日立の各社アーキテクチャーに違いはありません。
整数は、多くのコンパイラー言語ではint型と呼ばれますが、メインフレームのアセンブラーでは integer と言う表現はあまり行われず固定小数点とかバイナリーとか呼ばれます。アセンブラー言語が規定した型名で呼ばれる場合もあります。例えば、F型やH型などです。

    S/370アーキテクチャーで取り扱う整数
    タイプ 長さ 表現できる数値
    ハーフワード(H) 符号ビット+15ビット -2^15~2^15-1
    (-32,768~32,767)
    フルワード(F) 符号ビット+31ビット -2^31~2^31-1
    (-2,147,483,648~2,147,483,647)
    ダブルワード(D) 符号ビット+63ビット -2^63~2^63-1
    (長すぎるので省略)

フルワード(4バイト)整数については、1部の命令で符号無し32ビット整数として取り扱うこともできる。ダブルワード整数については、乗算の積および除算の被除数としてのみ扱うことができる。一般的に、メインフレームの整数と言えばハーフワードまたはフルワードで考えればよい。C言語で言うshort(16ビット)とlong(32ビット)になる。int型でも32ビットで考えればよい。ちなみにCPUも32ビットCPUである。

符号付き32ビット整数ではマイナス値(負数)は2の補数で表現します。+1はx00000001、0はx00000000ですが、-1はxFFFFFFFFです。2の補数は、ビット補数で簡単に求めることができます。正の数を2進数で表現し、すべての1を0に、0を1にひっくり返します。それに1を足せばよいのです。他の計算方法もありますが、こちらがいちばん簡単でしょう。

実際は自分で計算しなくても補数を取るCPU命令が使えます。ただ、内部でこう表現されていることを知っているといろいろ便利なこともあります。

文字の表現(キャラクター)

S/370では文字はバイトで表現されます。1文字が1バイト(8bit)です。1バイトの2進数をどの文字に割り当てるかの対応を示すのが文字コードセットです。メインフレームではEBCDIC文字コードセットが使われ、数字文字0~9にはxF0~xF9が割り当てられます。ASCIIと異なり英文字は数字文字より小さなコード値(x81~xE9)が割り当てられます。文字をソートした時の並び順やゾーン10進数のゾーン・ビットのパターンが異なることは知っておく必要があります。
メモリー上のデータが数値(整数)なのか文字なのかは、そのデータを処理する命令によって決まります。演算命令を使えば整数と見なされますし、文字操作命令を使えば文字、文字列と見なされます。

    EBCDIC文字コード表(カタカナ文字セット:IBM930)
    x40 x50 x60 x70 x80 x90 xA0 xB0 xC0 xD0 xE0 xF0
    0 空白 &
    1
    2
    3
    4
    5
    6
    7
    8
    9
    A
    B
    C
    D _
    E
    F

数の表現(パック10進数とゾーン10進数)

S/370では2進整数の他に10進数を扱うこともできます。命令で直接数値演算ができる形式がパック10進数、表示や印刷など人間が見てわかる文字形式に変換されたものがゾーン10進数(アンパック10進数)です。

    バイト バイト バイト バイト
    0 1 2 3 4 5 6 C
    上記はパック10進数で+123,456を示す例。数値は4ビットずつに区切られ、0~9の値が入る。末尾の4ビットは符号で、+はC、-はDで示す。+はFでもかまわない。最大16バイト(31桁+符号)の長さを扱うことができる。

    バイト バイト バイト バイト
    F 1 F 2 F 3 C 4
上記はゾーン10進数で+1,234を示す例。数値は8ビットずつに区切られ、先頭4ビットにゾーンビット(xF)、後部4ビットに0~9の値が入る。末尾バイトの先頭4ビットは符号で、+はC、-はDで示す。+はFでもかまわない。パック10進数を演算した結果は命令でゾーン10進数に変換できるが符号はそのままCまたはDで設定される。末尾桁を表示する際にはプログラムで符号ビットを変換するなどして表示可能な文字列に編集する必要がある。

これから新たに事務計算用のプログラムをアセンブラーで作ることはないでしょうが、システム系プログラムでも2進整数の演算結果を人間がわかる形に編集してメッセージなどを組み立てて出力することはよくおこなわれます。このような時はパックやゾーン10進数がデータ変換のために使われます。

数の表現(浮動小数点)

    符号
    1bit
    指数部
    7bit
    仮数部(小数部)
    24 or 56 or 112bit

S/370での浮動小数点は、上図のように表現されるExcess-64方式と呼ばれる16進浮動小数点です(仮数部の長さによって短精度、長精度、拡張精度の3種類があり、16進数でそれぞれ6、14、28桁となる)。符号は正負を表し0が+、1が-です。仮数部は1以下の正の数を表し、小数点が仮数部の直前(ビット8の直前)にあるものとされます。指数部は16の累乗値を表し7ビットの2進数値から64を引いた値がべき数です。この表現方法がExcess-64です。0~127の指数値は-64~+63のべき数に対応します。
浮動小数点値は、仮数部に指数部で示すべき数を使った16のべき乗の値を掛けたものです。仮数部*16^指数部で示すべき数になります。ESA/390アーキテクチャーではさらに別の方式である2進浮動小数点(インテルCPUなどで使われている)、zアーキテクチャーでは10進浮動小数点もサポートされています。
これ以上はややこしくなるので解説を省きますが、浮動小数点は2^31乗で示される±約21億4700万の整数演算では処理できない大きな数や小数点以下の値の演算が必要な科学技術計算などで使用されます。ただし、百分率(%)や平均値などで計算結果を小数点表示(例えば1÷3を0.33と表示)したい場合などでは整数演算して表示のしかただけ工夫したり、事務計算で大きな数を扱う場合はパック10進数を使うことも多く、システム系プログラムや事務処理系のアプリケーションで浮動小数点が使われることは殆どありません。とりあえずは浮動小数点というものの存在だけ知っておけばいいでしょう。将来本当に必要になる機会が来たら調べてください(ちなみに私は1度ぐらいしか使ったことがありません)。

2024年1月追記:±約21億を超える数値に関しては64ビットのzアーキテクチャーであれば64ビット整数が扱えるので、今日では小数点以下も処理する必要がある科学技術計算の分野などに適用業務が限られて来ています。アーキテクチャーが16進浮動小数点だけでなく2進や10進浮動小数点をサポートしたのは、z/Linuxなどz/OS以外のシステムでの必要性が大きいこともあるでしょう。