演算割り当てのルール

このドキュメントでは、次のトピックについて説明します。


フィールドの初期化

算術演算のオペランドとして使用するフィールド(ユーザー定義変数またはデータベースフィールド)は、以下のフォーマットのいずれかで定義する必要があります。

Format
N アンパック型数値
P パック型数値
I 整数
F 浮動小数点
D 日付
T 時刻

注意:
レポーティングモードの場合:算術演算のオペランドとして使用するフィールドをあらかじめ定義しておく必要があります。算術演算の結果フィールドとして使用するユーザー定義変数またはデータベースフィールドについては、あらかじめ定義しておく必要はありません。

DEFINE DATA ステートメントで定義されているユーザー定義変数およびデータベースフィールドはすべて、実行するためにプログラムが呼び出されるときに、ゼロまたは空白で適切に初期化されます。

データ転送

データ転送は、MOVE ステートメントまたは COMPUTE ステートメントを使用して実行します。次の表は、オペランドで使用可能な、データ転送で互換性のあるフォーマットをまとめたものです。

送出側のフィールドフォーマット 受け取り側のフィールドフォーマット
N または P A U Bn (n<5) Bn (n>4) I L C D T F G O
N または P [ 2 ] [ 14 ] [ 3 ] - - - - - -
A - [ 13 ] [ 1 ] [ 1 ] - - - - - - - -
U - [ 11 ] [ 12 ] [ 12 ] - - - - - - - -
Bn (n<5) [ 4 ] [ 2 ] [ 14 ] [ 5 ] [ 5 ] - - - - -
Bn (n>4) - [ 6 ] [ 15 ] [ 5 ] [ 5 ] - - - - - - - -
I [ 2 ] [ 14 ] [ 3 ] - - - - - -
L - [ 9 ] [ 16 ] - - - - - - - - -
C - - - - - - - - - - - -
D [ 9 ] [ 16 ] - - - [7] - -
T [ 9 ] [ 16 ] - - - [ 8 ] - -
F [ 9 ] [ 10 ] [ 10 ] [ 16 ] [ 3 ] - - - - - -
G - - - - - - - - - - - -
O - - - - - - - - - - - -

上記の意味は次に示すとおりです。

データ転送の互換性があることを示します。
- データ転送の互換性がないことを示します。
[ ] 角カッコ [ ] 内の数字は、下記の対応するデータ転送ルールを参照します。

データ変換

データ値の変換には、以下のルールが適用されます。

  1. 英数字からバイナリ
    値は左から右に 1 バイトずつ移動します。定義されている長さおよび指定されているバイト数に応じて、変換後の値は切り捨てられるか、または末尾に空白文字が追加されます。

  2. (N、P、I)およびバイナリ(長さ 1~4)から英数字
    値はアンパック形式に変換され、左詰めで英数字フィールドに転送されます。つまり、先行ゼロは除去され、フィールドには末尾の空白が充填されます。負の数値に関しては、記号は 16 進法 Dx に変換されます。数値の小数点は無視されます。小数点の前および後のすべての桁は、1 つの整数値として扱われます。

  3. (N、P、I)からバイナリ(1~4 バイト)
    数値はバイナリ(4 バイト)に変換されます。数値の小数点は無視され、小数点の前後の値は 1 つの整数値として扱われます。変換後のバイナリ値は、符号の値に応じて、正数または 2 の補数になります。

  4. バイナリ(1~4 バイト)から数値
    変換された値は右詰めで数値に割り当てられます。つまり、先行ゼロを含みます長さが 1~3 バイトのバイナリ値は、常に正の符号を使用しているとみなされます。長さが 4 バイトのバイナリ値の場合、最も左側のビットによって数値の符号が決まります(1 = 負、0 = 正)。受け取り側の数値の小数点は無視されます。小数点の前および後のすべての桁は、1 つの整数値として扱われます。

  5. バイナリからバイナリ
    値は右から左に 1 バイトずつ転送されます。受け取りフィールドには、先頭にバイナリゼロが挿入されます。

  6. バイナリ(5 バイト以上)から英数字
    値は左から右に 1 バイトずつ移動します。定義されている長さおよび指定されているバイト数に応じて、変換後の値は切り捨てられるか、または末尾に空白文字が追加されます。

  7. 日付(D)から時刻(T)
    日付を時刻に転送する場合、時刻は 00:00:00:0 として変換されます。

  8. 時刻(T)から日付(D)
    時刻を日付に転送する場合、時刻情報は切り捨てられ、日付情報のみが残されます。

  9. L、D、T、F から A
    値は表示形式に変換され、左詰めで割り当てられます。

  10. F
    短い英数字フィールドまたは Unicode フィールドに F を割り当てる場合、必要に応じて仮数が縮小されます。

  11. Unicode から英数字
    Unicode 値は、International Components for Unicode(ICU)ライブラリを使用して、デフォルトのコードページ(システム変数 *CODEPAGE の値)に基づいた英数字に変換されます。定義された長さおよび指定されたバイト数に応じて、結果が切り捨てられるか、結果の末尾に空白文字が追加されます。

  12. Unicode からバイナリ
    値は左から右にコード単位で転送されます。定義された長さおよび指定されたバイト数に応じて、結果が切り捨てられるか、結果の末尾に空白文字が追加されます。受け取り側のバイナリフィールドの長さは偶数である必要があります。

  13. 英数字から Unicode
    英数字値は、International Components for Unicode(ICU)ライブラリを使用して、デフォルトのコードページから Unicode 値に変換されます。定義された長さおよび指定されたコード単位数に応じて、結果が切り捨てられるか、結果の末尾に空白文字が追加されます。

  14. (N、P、I)およびバイナリ(長さ 1~4)から Unicode
    値はアンパック形式に変換され、その値から先行ゼロを除去した英数字値が取得されます。負の数値に関しては、記号は 16 進法 Dx に変換されます。数値の小数点は無視されます。小数点の前および後のすべての桁は、1 つの整数値として扱われます。変換後の値が、英数字から Unicode に変換されます。定義された長さおよび指定されたコード単位数に応じて、結果が切り捨てられるか、結果の末尾に空白文字が追加されます。

  15. バイナリ(5 バイト以上)から Unicode
    値は左から右に 1 バイトずつ移動します。定義されている長さおよび指定されているバイト数に応じて、変換後の値は切り捨てられるか、または末尾に空白文字が追加されます。送出側のバイナリフィールドの長さは偶数である必要があります。

  16. L、D、T、F から U
    値は英数字の表示形式に変換されます。変換後の値が、英数字から Unicode に変換され、左詰めで割り当てられます。

ソースとターゲットのフォーマットが同じ場合、定義されている長さと、指定されているバイト数(フォーマット A および B)またはコード単位(フォーマット U)に応じて、変換後の値は切り捨てられるか、あるいは末尾の空白(フォーマット A および U)または先頭のバイナリゼロ(フォーマット B)が追加されます。

ダイナミック変数の使用」も参照してください。

フィールドの切り捨てと切り上げ

フィールドの切り捨ておよび切り上げには、以下のルールが適用されます。

  • 数値フィールドの上位の切り捨ては、切り捨てる桁が先行ゼロの場合にのみ実行できます。小数点以下の桁は、小数点が明示的に指定されているかどうかにかかわらず、切り捨てられます。

  • 英数字フィールドの末尾の空白は切り捨てられます。

  • オプション ROUNDED を指定すると、切り捨てられる値の一番上の桁が 5 以上の場合、結果の一番下の桁に切り上げられます。除算の結果の精度については、「算術演算結果の精度」も参照してください。

算術演算結果のフォーマットと長さ

次の表は、算術演算の結果のフォーマットと長さを示しています。

  I1 I2 I4 N または P F4 F8
I1 I1 I2 I4 P* F4 F8
I2 I2 I2 I4 P* F4 F8
I4 I4 I4 I4 P* F4 F8
N または P P* P* P* P* F4 F8
F4 F4 F4 F4 F4 F4 F8
F8 F8 F8 F8 F8 F8 F8

メインフレームコンピュータでは、算術演算結果の精度を向上させるため、フォーマット/長さ F4 の代わりに F8 が使用されます。

P* は、「算術演算結果の精度」に示されているように、各オペランドの整数の長さと精度に基づいて個別に決定されます。

フォーマット I に適用される、10 進数での桁数と有効な値を以下に示します。

フォーマット/長さ 10 進数での桁数 指定可能な値
I1 3 -128127
I2 5 -3276832767
I4 10 -21474836482147483647

浮動小数点数を使用した算術演算

以下では次のトピックについて説明します。

全般的な考慮事項

浮動小数点数(フォーマット F)は整数(フォーマット I)と同様に 2 の累乗の和として表されますが、アンパック型およびパック型の数値(フォーマット N および P)は 10 の累乗の和として表されます。

アンパック型またはパック型の数値では、小数点の位置は固定です。一方、浮動小数点数では、小数点の位置は(その名が示すとおり)"浮動" です。つまり、小数点の位置は固定されず、実際の値によって変わります。

浮動小数点数は、正弦関数や対数関数などの三角関数や算術関数の計算に不可欠です。

浮動小数点数の精度

浮動小数点数の性質から、精度には以下の制限があります。

  • フォーマット/長さが F4 の変数の場合、精度は約 7 桁に制限されます。

  • フォーマット/長さが F8 の変数の場合、精度は約 15 桁に制限されます。

これ以上大きな桁を持つ値は、浮動小数点数では正確に表せません。小数点の前後に何桁あるかに関係なく、浮動小数点数で対応できるのはそれぞれ先頭の 7 桁または 15 桁のみです。

整数値は、絶対値が 2 24 -1 を超えていなければ、フォーマット/長さが F4 の変数で正確に表すことができます。

浮動小数点表現への変換

英数字、アンパック型数値、またはパック型数値を浮動小数点フォーマットに変換する場合(割り当て操作など)、表現を変更、つまり、10 の累乗の和から 2 の累乗の和に変換する必要があります。

したがって、2 の累乗の有限和で表現できる数値のみが正確に表されます。それ以外の数値は近似値としてのみ表されます。

例:

以下の数値は、正確な浮動小数点表現です。

 1.25 = 20 + 2-2

以下の数値は、正確に表現できない浮動小数点表現です。

 1.2 = 20 + 2-3 + 2-4 + 2-7 + 2-8 + 2-11 + 2-12 + ...

このように、英数字、アンパック型数値、またはパック型数値から浮動小数点値への変換、およびその逆の変換では、小規模なエラーが発生する場合があります。

プラットフォーム依存

ハードウェアアーキテクチャが異なるため、浮動小数点数の表現はプラットフォームによって変わります。浮動小数点演算を行う同じアプリケーションを別のプラットフォームで実行すると、結果がわずかに異なるのはこのためです。それぞれの表現によって、浮動小数点変数の有効な値の範囲も決まります。(おおよその)有効な値の範囲は、次のとおりです。

  • F4 の変数に対し、±1.17 * 10-38~±3.40 * 1038

  • F8 の変数に対し、±2.22 * 10-308~±1.79 * 10308

注意:
電卓で使用されている表現も、コンピュータで使用されている表現と異なっている可能性があります。したがって、同じ計算をしても違う結果が出る可能性があります。

日付および時刻を使用した算術演算

フォーマット D(日付)および T(時刻)は、加算、減算、乗算、および除算でのみ使用できます。乗算および除算は、加算および減算の中間結果にのみ使用できます。

日付/時刻値は、相互に加算/減算できます。また、整数値(小数なし)を日付/時刻に加算/減算することもできます。これらの整数値は、フォーマット N、P、I、D、または T のフィールドに格納できます。

加算/減算の中間結果は、その後の演算で被乗数/被除数として使用することもできます。

日付値に加算/減算する整数値は、日数とみなされます。時刻値に加算/減算する整数値は、1/10 秒とみなされます。

日付と時刻を使用した算術演算には、以下のような Natural の内部処理に基づく制限が適用されます。

Natural では内部的に、日付/時刻変数を使用した算術演算は、以下のように処理されます。

COMPUTEresult-field = operand1 +/- operand2

上のステートメントは次のように解決されます。

  1. intermediate-result = operand1 +/- operand2

  2. result-field = intermediate-result

つまり、Natural は、最初の手順で加算/減算の結果を計算し、2 番目の手順でその結果を結果フィールドに割り当てます。

さらに複雑な算術演算も、次のように、同じパターンに従って解決されます。

COMPUTEresult-field = operand1 +/- operand2 +/- operand3 +/- operand4

上のステートメントは次のように解決されます。

  1. intermediate-result1 = operand1 +/- operand2

  2. intermediate-result2 = intermediate-result1 +/- operand3

  3. intermediate-result3 = intermediate-result2 +/- operand4

  4. result-field = intermediate-result3

乗算および除算も、加算および減算と同様に解決されます。

このような中間結果の内部フォーマットは、以下の表に示すとおり、オペランドのフォーマットに依存します。

Addition

以下の表は、加算(intermediate-result = operand1 + operand2)の中間結果のフォーマットを示しています。

operand1 のフォーマット operand2 のフォーマット intermediate-result のフォーマット
D D Di
D T T
D Di、Ti、N、P、I D
T D、T、Di、Ti、N、P、I T
Di、Ti、N、P、I D D
Di、Ti、N、P、I T T
Di、N、P、I Di Di
Ti、N、P、I Ti Ti
Di Ti、N、P、I Di
Ti Di、N、P、I Ti

減算

以下の表は、減算(intermediate-result = operand1 - operand2)の中間結果のフォーマットを示しています。

operand1 のフォーマット operand2 のフォーマット intermediate-result のフォーマット
D D Di
D T Ti
D Di、Ti、N、P、I D
T D、T Ti
T Di、Ti、N、P、I T
Di、N、P、I D Di
Di、N、P、I T Ti
Di Di、Ti、N、P、I Di
Ti D、T、Di、Ti、N、P、I Ti
N、P、I Di、Ti P12

乗算または除算

以下の表は、乗算(intermediate-result = operand1 * operand2)または除算(intermediate-result = operand1 / operand2)の中間結果のフォーマットを示しています

operand1 のフォーマット operand2 のフォーマット intermediate-result のフォーマット
D D、Di、Ti、N、P、I Di
D T Ti
T D、T、Di、Ti、N、P、I Ti
Di T Ti
Di D、Di、Ti、N、P、I Di
Ti D Di
Ti Di、T、Ti、N、P、I Ti
N、P、I D、Di Di
N、P、I T、Ti Ti

内部割り当て

Di は内部日付フォーマットの値です。Ti は内部時刻フォーマットの値です。これらの値は、その後の日付/時刻の算術演算に使用できますが、フォーマット D の結果フィールドに割り当てることはできません(以下の割り当て表を参照)。

内部フォーマット Di または Ti の中間結果をその後の加算/減算/乗算/除算でオペランドとして使用する複雑な算術演算では、これらのフォーマットはそれぞれ D または T とみなされます。

以下の表は、どの中間結果をどの結果フィールドに内部的に割り当て(result-field = intermediate-result)できるのかを示しています。

result-field のフォーマット intermediate-result のフォーマット Assignment possible
D D、T
D Di、Ti、N、P、I ×
T D、T、Di、Ti、N、P、I
N、P、I D、T、Di、Ti、N、P、I

フォーマット D または T の結果フィールドに、負数を格納することはできません。

例 1 および 2(無効):

COMPUTE DATE1 (D) = DATE2 (D) + DATE3 (D)
COMPUTE DATE1 (D) = DATE2 (D) - DATE3 (D)

加算/減算の中間結果はフォーマット Di ですが、フォーマット Di の値をフォーマット D の結果フィールドに割り当てることはできないため、これらの演算は実行できません。

例 3 および 4(無効):

COMPUTE DATE1 (D) = TIME2 (T) - TIME3 (T)
COMPUTE DATE1 (D) = DATE2 (D) - TIME3 (T)

加算/減算の中間結果はフォーマット Ti ですが、フォーマット Ti の値をフォーマット D の結果フィールドに割り当てることはできないため、これらの演算は実行できません。

例 5(有効):

COMPUTE DATE1 (D) = DATE2 (D) - DATE3 (D) + TIME3 (T)

この演算は可能です。まず、DATE3DATE2 から減算され、フォーマット Di の中間結果が生成されます。次に、この中間結果が TIME3 に加算され、フォーマット T の中間結果が生成されます。最後に、この 2 番目の中間結果が結果フィールド DATE1 に割り当てられます。

例 6 および 7(無効):

COMPUTE DATE1 (D) = DATE2 (D) + DATE3 (D) * 2
COMPUTE TIME1 (T) = TIME2 (T) - TIME3 (T) / 3

中間結果ではなく日付/時刻フィールドを使用して乗算/除算を実行しようとしているため、これらの演算は実行できません。

例 8(有効):

COMPUTE DATE1 (D) = DATE2 (D) + (DATE3(D) - DATE4 (D)) * 2

この演算は可能です。まず、DATE4DATE3 から減算され、フォーマット Di の中間結果が生成されます。次に、この中間結果が 2 で乗算され、フォーマット Di の中間結果が生成されます。この中間結果が DATE2 に加算され、フォーマット D の中間結果が生成されます。最後に、この 3 番目の中間結果が結果フィールド DATE1 に割り当てられます。

フォーマット T の値をフォーマット D のフィールドに割り当てる場合、時刻値に有効な日付コンポーネントが含まれていることを確認する必要があります。

フォーマット表現の混在に対するパフォーマンスの考慮事項

算術演算を実行する場合、フィールドフォーマットの選択はパフォーマンスに大きく影響します。

業務演算では、可能であれば、フォーマット I(整数)のフィールドのみを使用します。

科学演算では、可能であれば、フォーマット F(浮動小数点)のフィールドを使用します。

数値(N、P)と浮動小数点(F)のフォーマットが混在する式では、浮動小数点フォーマットへの変換が実行されます。この変換は CPU にとってかなりの負荷となります。したがって、算術演算ではフォーマット表現を混在させないことをお勧めします。

算術演算結果の精度

オペレーション 整数部の桁数 小数部の桁数
加算/減算 Fi + 1 または Si + 1(どちらか大きい方) Fd または Sd(どちらか大きい方)
乗算 Fi + Si
  • Fd + Sd が MAXPREC 未満の場合:Fd + Sd

  • Fd + Sd が MAXPREC 以上の場合:Fd、Sd または MAXPREC(いずれか大きい方)

除算 Fi + Sd (下記を参照)
累乗 29 - Fd(以下の「例外」を参照) Fd

各項目の意味を次に示します。

F 第 1 オペランド
S 第 2 オペランド
R Result
i 整数部の桁数
d 小数部の桁数

例外:

指数に 1 桁以上の小数部がある場合、累乗は内部的に浮動小数点フォーマットで行われ、その結果もまた浮動小数点フォーマットになります。詳細については、「浮動小数点数を使用した算術演算」を参照してください。

除算の結果に対する小数部の桁数

除算の結果の精度は、結果フィールドを使用できるかどうかによって変わります。

  • 結果フィールドを使用できる場合、精度は Fd または Rd(どちらか大きい方)になります*

  • 結果フィールドを使用できない場合、精度は Fd または Sd(どちらか大きい方)になります*

* ROUNDED オプションを使用すると、実際に結果を四捨五入する前に、結果の精度が内部的に 1 桁増やされます。

結果フィールドは、COMPUTE ステートメント、DIVIDE ステートメント、および比較演算子の後に除算が使用されている論理条件で使用できる、または使用できるとみなされます(例:IF #A = #B / #C THEN ...)。

結果フィールドは、比較演算子の前に除算が使用されている論理条件で使用できない、または使用できないものとみなされます(例:IF #B / #C = #A THEN ...)。

例外:

被除数と除数がともに整数フォーマットで、少なくともその 1 つが変数の場合、結果フィールドの精度および ROUNDED オプションが使用されているかどうかに関係なく、除算の結果は常に整数フォーマットになります。

算術式の結果の精度

算術式の精度。例:#A / (#B * #C) + #D * (#E - #F + #G)、算術演算の結果を処理順序で評価することによって導出されます。算術式の詳細については、COMPUTE ステートメントの説明にある arithmetic-expression を参照してください。

算術演算のエラー条件

加算、減算、乗算、および除算では、結果の総桁数(整数部と小数部)が 31 を超えるとエラーが発生する場合があります。

累乗では、以下の条件のいずれかに該当するとエラーが発生します。

  • 基数が小数部のあるパックフォーマットで(P3.2 など)、指数が 16 より大きい場合。

  • 基数が浮動小数点で、結果が概算で 7 * 1075 を超える場合。

配列の処理

通常、次の規則が適用されます。

  • スカラ演算はすべて、単一のオカレンスを構成する配列要素に適用されます。

  • 定数値を使用して変数を定義すると(#FIELD (I2) CONSTANT <8> など)、コンパイル時に値が変数に割り当てられ、その変数は定数として処理されます。したがって、このような変数を配列の添字として使用すると、その次元は特定の数のオカレンスを持つことになります。

  • 割り当て/比較操作で次元数の異なる 2 つの配列を使用する場合、次元数の少ない配列の "足りない" 次元は (1:1) とみなされます。

    例:#ARRAY1 (1:2)#ARRAY2 (1:2,1:2) に割り当てられている場合、#ARRAY1#ARRAY1 (1:1,1:2) であるとみなされます。

以下では次のトピックについて説明します。

配列の次元の定義

1 次元、2 次元、および 3 次元の配列は、以下のように定義します。

次元数 プロパティ
3 #a3(第 3 次元, 第 2 次元, 第 1 次元)
2 #a2(第 2 次元, 第 1 次元)
1 #a1(第 1 次元)

配列の割り当て操作

配列の範囲を別の配列の範囲に割り当てる場合、割り当ては要素ごとに実行されます。

例:

DEFINE DATA LOCAL
1 #ARRAY(I4/1:5) INIT <10,20,30,40,50>
END-DEFINE
*
MOVE #ARRAY(2:4) TO #ARRAY(3:5)
/* is identical to
/* MOVE #ARRAY(2) TO #ARRAY(3)
/* MOVE #ARRAY(3) TO #ARRAY(4)
/* MOVE #ARRAY(4) TO #ARRAY(5)
/*  
/* #ARRAY contains  10,20,20,20,20

単一のオカレンスを配列の範囲に割り当てる場合、範囲の各要素に単一のオカレンスの値が割り当てられます。算術関数を使用する場合、範囲の各要素に関数の結果が割り当てられます。

割り当て操作を実行する前に、配列の各次元を相互に比較して、以下にリストされている条件に該当するかどうかをチェックします。次元は個別に比較されます。つまり、配列の第 1 次元はもう 1 つの配列の第 1 次元と、配列の第 2 次元はもう 1 つの配列の第 2 次元と、そして配列の第 3 次元はもう 1 つの配列の第 3 次元と比較されます。

ある配列から別の配列への値の割り当ては、以下の条件のいずれかに該当する場合にのみ実行できます。

  • 比較する次元のオカレンス数が一致している場合

  • 比較する次元の両方のオカレンス数が無限の場合

  • 別の次元に割り当てられる次元が単一のオカレンスで構成されている場合

例 - 配列の割り当て:

以下のプログラムは、実行可能な配列の割り当て操作を示しています。

 DEFINE DATA LOCAL
 1 A1   (N1/1:8)
 1 B1   (N1/1:8)
 1 A2   (N1/1:8,1:8)
 1 B2   (N1/1:8,1:8)
 1 A3   (N1/1:8,1:8,1:8)
 1 I    (I2)          INIT <4>
 1 J    (I2)          INIT <8>
 1 K    (I2)          CONST <8>
 END-DEFINE
 *
 COMPUTE A1(1:3) = B1(6:8)                                      /* allowed
 COMPUTE A1(1:I) = B1(1:I)                                      /* allowed
 COMPUTE A1(*)   = B1(1:8)                                      /* allowed
 COMPUTE A1(2:3) = B1(I:I+1)                                    /* allowed
 COMPUTE A1(1)   = B1(I)                                        /* allowed
 COMPUTE A1(1:I) = B1(3)                                        /* allowed
 COMPUTE A1(I:J) = B1(I+2)                                      /* allowed
 COMPUTE A1(1:I) = B1(5:J)                                      /* allowed
 COMPUTE A1(1:I) = B1(2)                                        /* allowed
 COMPUTE A1(1:2) = B1(1:J)                                      /* NOT ALLOWED (NAT0631)
 COMPUTE A1(*)   = B1(1:J)                                      /* NOT ALLOWED (NAT0631)
 COMPUTE A1(*)   = B1(1:K)                                      /* allowed
 COMPUTE A1(1:J) = B1(1:K)                                      /* NOT ALLOWED (NAT0631)
 *
 COMPUTE A1(*)       = B2(1,*)                                  /* allowed
 COMPUTE A1(1:3)     = B2(1,I:I+2)                              /* allowed
 COMPUTE A1(1:3)     = B2(1:3,1)                                /* NOT ALLOWED (NAT0631)
 *
 COMPUTE A2(1,1:3)   = B1(6:8)                                  /* allowed
 COMPUTE A2(*,1:I)   = B1(5:J)                                  /* allowed
 COMPUTE A2(*,1)     = B1(*)                                    /* NOT ALLOWED (NAT0631)
 COMPUTE A2(1:I,1)   = B1(1:J)                                  /* NOT ALLOWED (NAT0631)
 COMPUTE A2(1:I,1:J) = B1(1:J)                                  /* allowed
 *
 COMPUTE A2(1,I)     = B2(1,1)                                  /* allowed
 COMPUTE A2(1:I,1)   = B2(1:I,2)                                /* allowed
 COMPUTE A2(1:2,1:8) = B2(I:I+1,*)                              /* allowed
 *
 COMPUTE A3(1,1,1:I)   = B1(1)                                  /* allowed
 COMPUTE A3(1,1,1:J)   = B1(*)                                  /* NOT ALLOWED (NAT0631)
  COMPUTE A3(1,1,1:I)   = B1(1:I)                               /* allowed
 COMPUTE A3(1,1:2,1:I) = B2(1,1:I)                              /* allowed
 COMPUTE A3(1,1,1:I)   = B2(1:2,1:I)                            /* NOT ALLOWED (NAT0631)
 END

配列の比較操作

一般的に、複数の次元を持つ配列を比較する場合、各次元は個別に処理されます。つまり、配列の第 1 次元はもう 1 つの配列の第 1 次元と、配列の第 2 次元はもう 1 つの配列の第 2 次元と、そして配列の第 3 次元はもう 1 つの配列の第 3 次元と比較されます。

2 つの配列の次元の比較は、以下の条件のいずれかに該当する場合にのみ実行できます。

  • 比較する配列の次元のオカレンス数が同じ場合

  • 比較する配列の次元のオカレンス数が無限の場合

  • どちらかの配列の全次元が単一のオカレンスの場合

例 - 配列の比較:

以下のプログラムは、実行可能な配列の比較操作を示しています。

 DEFINE DATA LOCAL
 1 A3  (N1/1:8,1:8,1:8)
 1 A2  (N1/1:8,1:8)

 1 A1  (N1/1:8)
 1 I   (I2)   INIT <4>
 1 J   (I2)   INIT <8>
 1 K   (I2)   CONST <8>
 END-DEFINE
 *
 IF A2(1,1)     = A1(1)               THEN IGNORE END-IF /* allowed
 IF A2(1,1)     = A1(I)               THEN IGNORE END-IF /* allowed
 IF A2(1,*)     = A1(1)               THEN IGNORE END-IF /* allowed
 IF A2(1,*)     = A1(I)               THEN IGNORE END-IF /* allowed
 IF A2(1,*)     = A1(*)               THEN IGNORE END-IF /* allowed
 IF A2(1,*)     = A1(I -3:I+4)        THEN IGNORE END-IF /* allowed
 IF A2(1,5:J)   = A1(1:I)             THEN IGNORE END-IF /* allowed
 IF A2(1,*)     = A1(1:I)             THEN IGNORE END-IF /* NOT ALLOWED(NAT0629)
 IF A2(1,*)     = A1(1:K)             THEN IGNORE END-IF /* allowed
 *
 IF A2(1,1)     = A2(1,1)             THEN IGNORE END-IF /* allowed
 IF A2(1,1)     = A2(1,I)             THEN IGNORE END-IF /* allowed
 IF A2(1,*)     = A2(1,1:8)           THEN IGNORE END-IF /* allowed
 IF A2(1,*)     = A2(I,I -3:I+4)      THEN IGNORE END-IF /* allowed
 IF A2(1,1:I)   = A2(1,I+1:J)         THEN IGNORE END-IF /* allowed
 IF A2(1,1:I)   = A2(1,I:I+1)         THEN IGNORE END-IF /* NOT ALLOWED(NAT0629)
 IF A2(*,1)     = A2(1,*)             THEN IGNORE END-IF /* NOT ALLOWED(NAT0629)
 IF A2(1,1:I)   = A1(2,1:K)           THEN IGNORE END-IF /* NOT ALLOWED(NAT0629)
 *
 IF A3(1,1,*)   = A2(1,*)             THEN IGNORE END-IF /* allowed
 IF A3(1,1,*)   = A2(1,I -3:I+4)      THEN IGNORE END-IF /* allowed
 IF A3(1,*,I:J) = A2(*,1:I+1)         THEN IGNORE END-IF /* allowed
 IF A3(1,*,I:J) = A2(*,I:J)           THEN IGNORE END-IF /* allowed
 END

2 つの配列の範囲を比較する場合、以下の 2 つの表現では結果が異なることに注意してください。

#ARRAY1(*) NOT EQUAL #ARRAY2(*) 
NOT #ARRAY1(*) = #ARRAY2(*)

例:

  • 条件 A:
    IF #ARRAY1(1:2) NOT EQUAL #ARRAY2(1:2)

    これは、以下と等しくなります。

    IF (#ARRAY1(1) NOT EQUAL #ARRAY2(1)) AND (#ARRAY1(2) NOT EQUAL #ARRAY2(2))

    したがって、#ARRAY1 の最初のオカレンスと #ARRAY2 の最初のオカレンスが異なり、かつ#ARRAY1 の 2 番目のオカレンスと #ARRAY2 の 2 番目のオカレンスが異なる場合に、条件 A は真になります。

  • 条件 B:
    IF NOT #ARRAY1(1:2) = #ARRAY2(1:2)

    これは、以下と等しくなります。

    IF NOT (#ARRAY1(1)= #ARRAY2(1) AND #ARRAY1(2) = #ARRAY2(2))
    

    また、以下とも等しくなります。

    IF (#ARRAY1(1) NOT EQUAL #ARRAY2(1)) OR (#ARRAY1(2) NOT EQUAL #ARRAY2(2))

    したがって、#ARRAY1 の最初のオカレンスと #ARRAY2 の最初のオカレンスが異なるか、または#ARRAY1 の 2 番目のオカレンスと #ARRAY2 の 2 番目のオカレンスが異なるかのいずれかの場合に、条件 B は真になります。

配列での算術演算

配列を使用する算術演算の一般的なルールは、対応する次元のオカレンス数が一致している必要があるということです。

以下の例は、このルールに従っています。

#c(2:3,2:4) := #a(3:4,1:3) + #b(3:5)

つまり、次のようになります。

配列 次元番号 オカレンス数 Range
#c 2nd 2 2:3
#c 1st 3 2:4
#a 2nd 2 3:4
#a 1st 3 1:3
#b 1st 3 3:5

演算は要素ごとに実行されます。

注意:
次元数の異なる算術演算を実行することもできます。

上記の例では、以下の演算が実行されます。

#c(2,2) := #a(3,1) + #b(3)

#c(2,3) := #a(3,2) + #b(4)

#c(2,4) := #a(3,3) + #b(5)

#c(3,2) := #a(4,1) + #b(3)

#c(3,3) := #a(4,2) + #b(4)

#c(3,4) := #a(4,3) + #b(5)

以下のリストは、(COMPUTEADD、および MULTIPLY の各ステートメントにおける)算術演算で配列の範囲がどのように使用されるかを例示したものです。例 1~4 では、対応する次元のオカレンス数が一致している必要があります。

  1. range + range = range.

    加算は要素ごとに実行されます。

  2. range * range = range.

    乗算は要素ごとに実行されます。

  3. scalar + range = range.

    スカラが範囲の各要素に加算されます。

  4. range * scalar = range.

    範囲の各要素がスカラで乗算されます。

  5. range + scalar = scalar.

    範囲の各要素がスカラに加算され、その結果がスカラに割り当てられます。

  6. scalar * range = scalar2.

    スカラが配列の各要素で乗算され、その結果が scalar2 に割り当てられます。

上記の例に示されているように、算術演算では中間結果が生成されるため、重複する添字範囲は要素ごとに計算され、その結果が中間結果配列に格納されます。そして最後に、中間結果配列が結果フィールドに割り当てられます。

例:

DEFINE DATA LOCAL
1 #ARRAY(I4/1:5) INIT <10,20,30,40,50>            
END-DEFINE

#ARRAY(3:5) := #ARRAY(2:4) + 1

/* A temporary array for the 
/* intermediate result values is 
/* generated implicitly: #temp(1:3).
/* The following operations are 
/* performed internally:                             
/* #temp(1) := #ARRAY(2) + 1
/* #temp(2) := #ARRAY(3) + 1
/* #temp(3) := #ARRAY(4) + 1
/* #ARRAY(3:5) := #temp(1:3)
/*  
/* #ARRAY contains  10,20,21,31,41