最新のリリースでは、このページがまだ翻訳されていません。 このページの最新版は英語でご覧になれます。

生成されたコードと MATLAB コードの相違点

MATLAB® コードを効率良く動作する C/C++ コードに変換するため、コード ジェネレーターに、元のソース コードと生成後のコードの動作が意図的に異なる (場合によっては異なる結果が出る) ように最適化が導入されています。

次のような相違点があります。

プログラムを実行すると、実行時エラー チェックで次の相違点が検出される可能性があります。既定では、実行時エラー チェックは MEX コードに対して有効化され、スタンドアロン C/C++ コードに対しては無効化されています。コードを配布する前に相違点を識別して解決できるように、コード ジェネレーターは、潜在的な相違として相違点のサブセットを報告します。

可能な出力が複数ある関数

行列の特異値分解や固有値分解などの特定の数学演算では、複数の解が得られる場合があります。このような演算を実装する 2 つの異なるアルゴリズムは、同じ入力値に対して異なる出力を返す場合があります。同じアルゴリズムの 2 つの異なる実装でも同じ動作になることがあります。

このような数学演算では、生成されたコードと MATLAB の対応する関数が同じ入力値に対して異なる出力を返す場合があります。関数がこの動作を示すかどうかを確認するには、対応する関数のリファレンス ページで、「拡張機能」「C/C++ コードの生成」セクションを参照してください。このような関数の例には、svdeig などがあります。

変数 ans への書き込み

出力引数を指定せずに出力を返す MATLAB コードを実行すると、MATLAB は変数 ans に出力を暗黙的に書き込みます。変数 ans が既にワークスペースに存在する場合、MATLAB はその値を返された出力に更新します。

このような MATLAB コードから生成されたコードは、変数 ans に出力を暗黙的に書き込みません。

たとえば、1 行目で変数 ans を明示的に作成する MATLAB 関数 foo を定義します。この関数は、2 行目が実行されると ans の値を暗黙的に更新します。

function foo %#codegen
ans = 1;
2;
disp(ans);
end

コマンド ラインで foo を実行します。ans の最終値 2 がコマンド ラインに表示されます。

foo
2

foo から MEX 関数を生成します。

codegen foo

生成された MEX 関数 foo_mex を実行します。この関数は変数 ans を明示的に作成し、その変数に値 1 を代入します。ただし、foo_mexans の値を 2 に暗黙的に更新しません。

foo_mex
1

ループ インデックスのオーバーフロー

for ループの終了値がループ インデックスのデータ型の最大値または最小値と等しいか近いと仮定します。生成コードでは、ループ インデックスの最後のインクリメントまたはデクリメントによって、インデックス変数のオーバーフローが発生する可能性があります。インデックスのオーバーフローの結果、無限ループが発生する可能性があります。

メモリの整合性チェックが有効になっている場合、ループ インデックスのオーバーフローが発生する可能性があることをコード ジェネレーターが検出すると、エラーが報告されます。ソフトウェアのエラー チェックは保守的です。ループ インデックスのオーバーフローを誤って報告する可能性があります。既定では、メモリの整合性チェックは MEX コードに対して有効化され、スタンドアロン C/C++ コードに対しては無効化されています。MATLAB で MEX 関数をテストする理由およびスタンドアロン C/C++ コードでの実行時エラーの検出とレポートを参照してください。

ループ インデックスのオーバーフローを回避するには、次の表の回避方法を使用してください。

オーバーフローを引き起こす可能性のあるループ条件回避方法
  • ループ インデックスが 1 ずつインクリメント

  • 終了値が整数データ型の最大値

ループがあらゆる整数データ型に対応する必要がない場合、終了値が整数データ型の最大値と等しくならないようにループを書き換えます。たとえば、

N=intmax('int16')
for k=N-10:N
次に置き換えます。
for k=1:10

  • ループ インデックスが 1 ずつデクリメント

  • 終了値が整数データ型の最小値

ループがあらゆる整数データ型に対応する必要がない場合、終了値が整数データ型の最小値と等しくならないようにループを書き換えます。たとえば、

N=intmin('int32')
for k=N+10:-1:N
次に置き換えます。
for k=10:-1:1

  • ループ インデックスが 1 ずつインクリメントまたはデクリメント

  • 開始値が整数データ型の最小値または最大値

  • 終了値が整数データ型の最大値または最小値

ループがあらゆる整数データ型に対応しなければならない場合は、ループの開始値、ステップ、および終了値の型をより大きな整数または double の値にキャストします。たとえば、次のコードを書き換えます。

M= intmin('int16');
N= intmax('int16');
for k=M:N
	% Loop body
end
以下のように書き換えます。
M= intmin('int16');
N= intmax('int16');
for k=int32(M):int32(N)
	% Loop body
end

  • ループ インデックスが 1 以外の値でインクリメントまたはデクリメント

  • 最後のループ反復において、ループ インデックスが終了値と等しくならない

ループを書き直し、最後のループ反復におけるループ インデックスが終了値と等しくなるようにします。

未入力の for ループのインデックス

MATLAB コードと生成されたコードでは、for ループの実行が完了した後、インデックス変数の値は for ループの最終反復時の値と等しくなります。

MATLAB でループが実行されない場合、インデックス変数の値は [ ] (空行列) として保存されます。生成されたコードでループが実行されない場合、インデックス変数の値は MATLAB のインデックス変数とは異なります。

  • for ループの開始変数と終了変数を実行時に指定する場合、インデックス変数の値は範囲の開始値と等しくなります。たとえば、以下の MATLAB コードについて考えます。

    function out = indexTest(a,b)
    for i = a:b
    end
    out = i;
    end

    ab1-1 として渡されると仮定します。for ループは実行されません。MATLAB では、out に [ ] が代入されます。生成されたコードでは、outa の値 1 が代入されます。

  • for ループの開始値と終了値をコンパイルの前に指定する場合、インデックス変数の値は 0 と等しくなります。以下の MATLAB コードについて考えます。

    function out = indexTest
    for i = 1:-1
    end
    out = i;
    end

    この関数を呼び出すと仮定します。MATLAB では、out に [ ] が代入されます。生成されたコードでは、out に値 0 が代入されます。

文字サイズ

MATLAB は 16 ビットの文字をサポートしていますが、生成されたコードでは文字を C 言語などの多くの埋め込み型言語の標準サイズである 8 ビットで表現します。コード生成での文字のエンコードを参照してください。

式の評価順番

生成されたコードは、式の評価順番を強制しません。ほとんどの式において、評価の順序は重要な意味をもちません。ただし、副作用を含む式では、生成コードと元の MATLAB コードとで副作用が異なる順序で生成される場合があります。副作用を伴う式には、以下のようなものがあります。

  • 永続変数またはグローバル変数を変更する

  • 画面にデータを表示する

  • データをファイルに書き込む

  • ハンドル クラスのオブジェクトのプロパティを変更する

また、生成されたコードは、ショートサーキットしない論理演算子の実行順番を強制しません。

結果を予測しやすくするために、実行順序に依存する式を複数のステートメントに分割してコードを書くことをお勧めします。

  • たとえば、次のコードを書き換えます。

    A = f1() + f2();

    以下のように書き換えます。

    A = f1();
    A = A + f2();

    こうすると、生成されたコードは f1 を呼び出してから、f2 を呼び出します。

  • 多出力関数呼び出しの出力を、相互に依存していない複数の変数に代入します。たとえば、次のコードを書き換えます。

    [y, y.f, y.g] = foo;

    これは、次のように書き換えられます。

    [y, a, b] = foo;
    y.f = a;
    y.g = b;
    

  • cell 配列の複数のセルの内容にアクセスする場合、その結果を相互に依存していない変数に代入します。たとえば、次のコードを書き換えます。

    [y, y.f, y.g] = z{:};
    

    これは、次のように書き換えられます。

    [y, a, b] = z{:};
    y.f = a;
    y.g = b;
    

関数ハンドルの作成中の名前解決

MATLAB とコード生成では、記号 @ の後の名前を解決する際に異なる優先順位規則に従います。これらの規則は無名関数には適用されません。優先順位規則を次の表にまとめます。

MATLAB での優先順位コード生成での優先順位
ピリオドを含まない式 (@x など)

入れ子関数、ローカル関数、プライベート関数、パス関数

ローカル変数、入れ子関数、ローカル関数、プライベート関数、パス関数

ピリオドを 1 つだけ含む式 (@x.y など)

ローカル変数、パス関数

ローカル変数、パス関数 (MATLAB と同じ)

複数のピリオドを含む式 (@x.y.z など)

パス関数

ローカル変数、パス関数

x がそれ自体が関数ハンドルとなるローカル変数である場合、生成されたコードと MATLAB では式 @x の解釈が異なります。

  • MATLAB ではエラーが発生します。

  • 生成されたコードでは @xx 自体の関数ハンドルとして解釈されます。

次の例では、2 つのピリオドを含む式の動作におけるこの相違点を示します。

現在の作業フォルダーにパッケージ x が含まれ、これに関数 z を含む別のパッケージ y が含まれていると仮定します。また、現在の作業フォルダーには、コードの生成対象となるエントリポイント関数 foo も含まれています。

Image of current folder showing the files z.m and foo.m with respect to the packages x and y.

ファイル foo の定義は次のとおりです。

function out = foo
    x.y.z = @()'x.y.z is an anonymous function';
    out = g(x);
end

function out = g(x)
    f = @x.y.z;
    out = f();
end

関数 z の定義は次のとおりです。

function out = z
    out = 'x.y.z is a package function';
end

foo の MEX 関数を生成します。生成された MEX 関数 foo_mex と MATLAB 関数 foo の両方を個別に呼び出します。

codegen foo
foo_mex
foo
ans =

    'x.y.z is an anonymous function'


ans =

    'x.y.z is a package function'

生成されたコードによって最初の出力が生成されます。MATLAB によって 2 番目の出力が生成されます。コード生成では、@x.y.zfoo で定義されているローカル変数 x に解決されます。MATLAB では、@x.y.z はパッケージ x.y 内の z に解決されます。

終了動作

生成されたコードの終了動作は、MATLAB ソース コードの動作と一致しません。たとえば、無限ループに副次的影響がない場合、無限ループは最適化によって生成されたコードから除去されます。その結果、対応する MATLAB コードが終了しなくても生成コードは終了する場合があります。

可変サイズ N 次元配列のサイズ

可変サイズの N 次元配列では、関数 size で返す結果が、生成されたコードと MATLAB ソース コードで異なる場合があります。関数 size は、生成されたコードでは後に 1 を続けて返すことがありますが (大きさが 1 の次元)、MATLAB では後続する 1 は常に切り捨てられます。たとえば、N 次元の配列 X が次元 [4 2 1 1] をもっている場合、size(X) は、生成されたコードでは [4 2 1 1] を返す場合がありますが、MATLAB では常に [4 2] を返します。可変サイズの N 次元配列のサイズを判定する上での MATLAB との非互換性を参照してください。

空配列のサイズ

空の配列のサイズは、生成コードと MATLAB ソース コードでは異なる場合があります。空配列のサイズを判定する際の MATLAB との非互換性を参照してください。

配列の要素を削除した結果として生じる空配列のサイズ

配列のすべての要素を削除することで、空の配列が生成されます。この空の配列のサイズは、生成コードと MATLAB ソース コードでは異なる場合があります。

ケースコード例MATLAB での空配列のサイズ生成コードでの空配列のサイズ
colon 演算子 (:) を使用して m 行 n 列の配列のすべての要素を削除する。
coder.varsize('X',[4,4],[1,1]);
X = zeros(2);
X(:) = [];
0-by-01-by-0
colon 演算子 (:) を使用して行ベクトルのすべての要素を削除する。
coder.varsize('X',[1,4],[0,1]);
X = zeros(1,4);
X(:) = [];
0-by-01-by-0
colon 演算子 (:) を使用して列ベクトルのすべての要素を削除する。
coder.varsize('X',[4,1],[1,0]);
X = zeros(4,1);
X(:) = [];
0-by-00-by-1
一度に 1 つずつ要素を削除して列ベクトルのすべての要素を削除する。
coder.varsize('X',[4,1],[1,0]);
X = zeros(4,1);
for i = 1:4
    X(1)= [];
end
1-by-00-by-1

single 型と double 型のオペランドをもつ要素単位の二項演算

MATLAB コードに single 型と double 型のオペランドをもつ要素単位の二項演算が含まれる場合、生成コードの結果が MATLAB の結果と同じにならないことがあります。

このような演算について、MATLAB では、両オペランドを double 型にキャストし、double 型で演算を実行します。MATLAB は実行結果を single 型にキャストし、その結果を返します。

生成コードでは、double 型のオペランドを single 型にキャストします。そして 2 つの single 型で演算を実行し、その結果を返します。

たとえば、要素単位の二項演算 plus を呼び出す MATLAB 関数 foo を定義します。

function out = foo(a,b)
out = a + b;
end

single 型の変数 s1 と、double 型の変数 v1 を定義します。single 型の入力と double 型の入力を受け入れる foo の MEX 関数を生成します。

s1 = single(1.4e32); 
d1 = -5.305e+32; 
codegen foo -args {s1, d1} 

入力 s1 および d1 を指定して、foofoo_mex の両方を呼び出します。2 つの結果を比較します。

ml = foo(s1,d1); 
mlc = foo_mex(s1,d1);
ml == mlc
ans =

  logical

   0

比較の出力は logical 0 になります。これは、これらの入力について、生成コードと MATLAB で異なる結果を生成することを示しています。

浮動小数点の数値結果

生成されたコードは、以下の場合の MATLAB における浮動小数点の数値結果とは、異なる可能性があります。

 コンピューター ハードウェアが拡張精度レジスタを使用する

 特定の高度なライブラリ機能

 BLAS ライブラリ関数の実装

NaN と無限大

生成されたコードでは、NaN および Inf の値が数学的に無意味の場合、値のパターンが MATLAB コードと異なる場合があります。たとえば、MATLAB 出力に NaN が含まれる場合、生成されたコードの出力にも NaN が含まれますが、必ずしも同じ場所に含まれるとは限りません。

NaN のビット パターンが MATLAB コードの出力と生成されたコードで異なる場合があります。それは、コードの生成に使用される C99 標準の数学ライブラリが、すべての実装で NaN に一意のビット パターンを指定しないからです。MATLAB 出力と SIL 出力または PIL 出力など異なる実装でビット パターンを比較しないでください。

負のゼロ

浮動小数点型の場合、値 0 は正の記号または負の記号のいずれかをもちます。算術的に、0-0 と等価ですが、一部の演算では 0 入力の符号が区別されます。たとえば、rdivideatan2atan2dangle がこれに該当します。0 で除算すると Inf が生成されますが、-0 で除算すると -Inf が生成されます。同様に、atan2d(0,-1) では 180 が生成されますが、atan2d (-0,-1) では -180 が生成されます。

浮動小数点変数が適切な範囲の整数値のみを取得することが、コード ジェネレーターによって検出される場合、コード ジェネレーターでは生成されたコード内の変数に整数型を使用できます。コード ジェネレーターで変数に整数型が使用されると、その変数は -0+0 として保存します。これは、整数型には値 0 の記号が保存されないためです。生成されたコードで変数が浮動小数点型にキャストし直されると、0 の記号は正になります。0 で除算すると -Inf ではなく Inf が生成されます。同様に、atan2d(0,-1) では -180 ではなく、180 が生成されます。

コード生成ターゲット

関数 coder.target は、生成されたコードと MATLAB で異なる値を返します。これは、関数が MATLAB で実行されているのか、それともシミュレーションまたはコード生成ターゲット用にコンパイルされたのかを判断できるようにすることを目的としています。coder.target を参照してください。

MATLAB クラス プロパティの初期化

MATLAB では、クラスの読み込み時にクラスの既定値を計算してからコード生成を行います。コード ジェネレーターでは、MATLAB で計算される値が使用されます。既定値は再計算されません。プロパティ定義で関数呼び出しを使って初期値を計算する場合、コード ジェネレーターはこの関数を実行しません。関数にグローバル変数や永続変数の変更といった副作用がある場合、生成されたコードから得られる結果が MATLAB の結果とは異なる可能性があります。詳細は、コード生成のためのクラス プロパティの定義を参照してください。

set メソッドをもつ入れ子のプロパティ割り当てにおける MATLAB クラス

ハンドル オブジェクトのプロパティに値を割り当てるときに、そのプロパティ自体が別のオブジェクトのプロパティでもある場合、生成されたコードは、MATLAB が呼び出さないハンドル クラスの set メソッドを呼び出すことができます。

たとえば、x がハンドル オブジェクト、pa がオブジェクト、pb がハンドル オブジェクト、pcpb のプロパティになるように、一連の変数を定義すると仮定します。以下のように、入れ子にされたプロパティの割り当てを行います。

x.pa.pb.pc = 0;

この場合、生成されたコードはオブジェクト pb の set メソッドと x の set メソッドを呼び出します。MATLAB は pb の set メソッドのみを呼び出します。

MATLAB ハンドル クラスのデストラクター

生成されたコードでのハンドル クラスのデストラクターの動作は、次の状況では MATLAB での動作とは異なる場合があります。

  • 複数の独立したオブジェクトを破棄する順序は、生成されたコードと MATLAB では異なる場合があります。

  • 生成されたコードでのオブジェクトの存続期間は、MATLAB での存続期間と異なる可能性があります。

  • 生成されたコードは、部分的に作成されたオブジェクトを破棄しません。ハンドル オブジェクトが実行時に完全に作成されていない場合、生成されたコードはエラー メッセージを作成しますが、そのオブジェクトの delete メソッドは呼び出しません。System object™ について、setupImpl で実行時エラーがある場合、生成されたコードは、そのオブジェクトの releaseImpl は呼び出しません。

    MATLAB は、delete メソッドを呼び出して、部分的に作成されたオブジェクトを破棄します。

詳細については、ハンドル クラス デストラクターのコード生成を参照してください。

可変サイズ データ

コード生成時の可変サイズのサポートに関する MATLAB との非互換性を参照してください。

複素数

複素数データのコード生成を参照してください。

連続する単項演算子をもつ string の double への変換

複数の連続する単項演算子を含む string を double に変換すると、MATLAB と生成されたコードで結果が異なる場合があります。次の関数を考えてみます。

function out = foo(op)
out = double(op + 1);
end

入力値 "--" の場合、関数によって string "--1"double に変換されます。MATLAB では、回答が NaN になります。生成されたコードの回答は 1 です。

関連するトピック