14

MATLAB/Octave でいくつかの VHDL コードの検証ツールに取り組んでいます。したがって、「実際の」オーバーフローを生成するデータ型が必要です。

intmax('int32') + 1
ans = -2147483648

あとで、変数のビット幅を定義できれば助かりますが、今はそれほど重要ではありません。

C に似た例を作成すると、変数が 0 より小さくなるまで増加し、永久に回転します。

test = int32(2^30);
while (test > 0)
    test = test + int32(1);
end

私が試した別のアプローチは、数値が変更されるたびに呼び出されるカスタム「オーバーフロー」ルーチンでした。このアプローチは非常に遅く、実用的ではなく、すべてのケースでまったく機能しませんでした。助言がありますか?

4

6 に答える 6

18

MATLAB では、整数データ型の算術演算を処理するメソッドをオーバーロードして、整数値の「ラップアラウンド」をもたらす独自のカスタム オーバーフロー動作を作成するオプションがあります。ドキュメントに記載されているように:

パス上のフォルダー内のフォルダーにint*適切な名前のメソッドを配置することにより、(任意のオブジェクトに対してできるように) に対して独自のメソッドを定義またはオーバーロードできます。オーバーロードできるメソッドの名前を@int*入力します。help datatypes

ドキュメントのこのページには、算術演算子の同等のメソッドがリストされています。バイナリ加算演算A+Bは、実際には関数によって処理されますplus(A,B)。したがって、 ( MATLAB パス@int32上の別のフォルダーに配置される) というフォルダーを作成し、データ型の組み込みメソッドの代わりに使用される関数をそこに配置できます。plus.mint32

plus必要なオーバーフロー/アンダーフロー動作を作成するためにオーバーロードされた関数を設計する方法の例を次に示します。

function C = plus(A,B)
%# NOTE: This code sample is designed to work for scalar values of
%#       the inputs. If one or more of the inputs is non-scalar,
%#       the code below will need to be vectorized to accommodate,
%#       and error checking of the input sizes will be needed.

  if (A > 0) && (B > (intmax-A))  %# An overflow condition

    C = builtin('plus',intmin,...
                B-(intmax-A)-1);  %# Wraps around to negative

  elseif (A < 0) && (B < (intmin-A))  %# An underflow condition

    C = builtin('plus',intmax,...
                B-(intmin-A-1));  %# Wraps around to positive

  else

    C = builtin('plus',A,B);  %# No problems; call the built-in plus.m

  end

end

ビルトインplusメソッド ( BUILTIN関数を使用)を呼び出して、int32オーバーフロー/アンダーフローの問題が発生しないことがわかっている値の加算を実行していることに注意してください。代わりに演算を使用して整数の加算を実行するA+Bと、オーバーロードされたメソッドへの再帰呼び出しが発生し、plus追加の計算オーバーヘッドまたは (最後の行が である最悪のシナリオではC = A+B;) 無限再帰につながる可能性があります。

これは、実際のラップアラウンド オーバーフロー動作を示すテストです。

>> A = int32(2147483642);  %# A value close to INTMAX
>> for i = 1:10, A = A+1; disp(A); end
  2147483643

  2147483644

  2147483645

  2147483646

  2147483647   %# INTMAX

 -2147483648   %# INTMIN

 -2147483647

 -2147483646

 -2147483645

 -2147483644
于 2010-03-12T03:12:02.067 に答える
5

C スタイルの数値演算を取得する場合は、MEX 関数を使用して C 演算子を直接呼び出すことができます。定義により、それらは C データ型のように機能します。

この方法は、 gnovice のオーバーライドよりもはるかに手間がかかりますが、大規模なコードベースによりよく統合され、組み込み型の定義を変更するよりも安全であるため、完全を期すために言及する必要があると思います。

以下は、Matlab 配列に対して C の "+" 操作を実行する MEX ファイルです。C スタイルの動作が必要な演算子ごとに、これらのいずれかを作成します。

/* c_plus.c - MEX function: C-style (not Matlab-style) "+" operation */

#include "mex.h"
#include "matrix.h"
#include <stdio.h>

void mexFunction(
                 int nlhs,       mxArray *plhs[],
                 int nrhs, const mxArray *prhs[]
                 )
{
    mxArray     *out;
    /* In production code, input/output type and bounds checks would go here. */
    const mxArray     *a = prhs[0];
    const mxArray     *b = prhs[1];
    int         i, n;
    int *a_int32, *b_int32, *out_int32;
    short *a_int16, *b_int16, *out_int16;

    mxClassID datatype = mxGetClassID(a);
    int n_a = mxGetNumberOfElements(a);
    int n_b = mxGetNumberOfElements(b);
    int         a_is_scalar = n_a == 1;
    int         b_is_scalar = n_b == 1;
    n = n_a >= n_b ? n_a : n_b;
    out = mxCreateNumericArray(mxGetNumberOfDimensions(a), mxGetDimensions(a),
            datatype, mxIsComplex(a));

    switch (datatype) {
        case mxINT32_CLASS:
            a_int32 = (int*) mxGetData(a);
            b_int32 = (int*) mxGetData(b);
            out_int32 = (int*) mxGetData(out);
            for (i=0; i<n; i++) {
                if (a_is_scalar) {
                    out_int32[i] = a_int32[i] + b_int32[i];
                } else if (b_is_scalar) {
                    out_int32[i] = a_int32[i] + b_int32[0];
                } else {
                    out_int32[i] = a_int32[i] + b_int32[i];
                }
            }
            break;
        case mxINT16_CLASS:
            a_int16 = (short*) mxGetData(a);
            b_int16 = (short*) mxGetData(b);
            out_int16 = (short*) mxGetData(out);
            for (i=0; i<n; i++) {
                if (a_is_scalar) {
                    out_int16[i] = a_int16[0] + b_int16[i];
                } else if (b_is_scalar) {
                    out_int16[i] = a_int16[i] + b_int16[0];
                } else {
                    out_int16[i] = a_int16[i] + b_int16[i];
                }
            }
            break;
        /* Yes, you'd have to add a separate case for every numeric mxClassID... */
        /* In C++ you could do it with a template. */
        default:
            mexErrMsgTxt("Unsupported array type");
            break;
    }

    plhs[0] = out;
}

次に、Matlab コードから呼び出す方法を理解する必要があります。すべてのコードを記述している場合は、どこでも "a + b" の代わりに "c_plus(a, b)" を呼び出すことができます。または、フィールドに Matlab 数値配列を保持し、適切な C スタイル MEX 関数を呼び出す plus() およびその他の操作を定義する、@cnumeric などの独自の数値ラッパー クラスを作成することもできます。

classdef cnumeric
    properties
        x % the underlying Matlab numeric array
    end
    methods
        function obj = cnumeric(x)
            obj.x = x;
        end

        function out = plus(a,b)
            [a,b] = promote(a, b); % for convenience, and to mimic Matlab implicit promotion
            if ~isequal(class(a.x), class(b.x))
                error('inputs must have same wrapped type');
            end
            out_x = c_plus(a.x, b.x);
            out = cnumeric(out_x);
        end

        % You'd have to define the math operations that you want normal
        % Matlab behavior on, too
        function out = minus(a,b)
            [a,b] = promote(a, b);
            out = cnumeric(a.x - b.x);
        end

        function display(obj)
            fprintf('%s = \ncnumeric: %s\n', inputname(1), num2str(obj.x));
        end

        function [a,b] = promote(a,b)
        %PROMOTE Implicit promotion of numeric to cnumeric and doubles to int
            if isnumeric(a); a = cnumeric(a); end
            if isnumeric(b); b = cnumeric(b); end
            if isinteger(a.x) && isa(b.x, 'double')
                b.x = cast(b.x, class(a.x));
            end
            if isinteger(b.x) && isa(a.x, 'double')
                a.x = cast(a.x, class(b.x));
            end
        end
    end

end

次に、C スタイルの int 動作が必要な @cnumeric で数値をラップし、それらを使用して計算します。

>> cnumeric(int32(intmax))
ans = 
cnumeric: 2147483647
>> cnumeric(int32(intmax)) - 1
ans = 
cnumeric: 2147483646
>> cnumeric(int32(intmax)) + 1
ans = 
cnumeric: -2147483648
>> cnumeric(int16(intmax('int16')))
ans = 
cnumeric: 32767
>> cnumeric(int16(intmax('int16'))) + 1
ans = 
cnumeric: -32768

プリミティブ @int32 型を壊すことから分離された、C スタイルのオーバーフロー動作があります。さらに、通常の数値を期待する他の関数に @cnumeric オブジェクトを渡すことができ、入力を多態的に処理する限り「機能」します。

パフォーマンス上の注意: これはオブジェクトであるため、+ はビルトインではなくメソッド ディスパッチの速度が遅くなります。大きな配列の呼び出しが少ない場合、実際の数値演算は C で行われるため、これは高速になります。小さな配列の呼び出しが多いと、メソッド呼び出しごとのオーバーヘッドが大きくなるため、速度が低下する可能性があります。

于 2010-03-15T19:01:42.650 に答える
1

次のコードスニペットを実行しました

test = int32(2^31-12);
for i = 1:24
    test = test + int32(1)
end

予期しない結果になります。Matlabの場合、intmax('int32')+1==intmax('int32')。私は2010aを64ビットのMacOSXで実行しています。

これが答えであるかどうかわからない場合は、Matlabが直感に反して動作することをさらに確認してください。ただし、intmax()関数のドキュメントには次のように記載されています。

intmaxによって返される値よりも大きい値は、32ビット整数にキャストされるとintmax値に飽和します。

したがって、Matlabは文書化されているとおりに動作していると思います。

于 2010-03-11T13:51:07.517 に答える
1

ええと...

実際、カスタムの「オーバーフロー」サブルーチンで問題を解決することができました...今では非常に遅くなりますが、予期しない動作はありません! Matlab/Octave では小さなエラーが発生するため、私の間違いは round() の欠落でした。

しかし、誰かがより速い解決策を知っているなら、私はそれを試してうれしいです!

function ret = overflow_sg(arg,bw)

    % remove possible rounding errors, and prepare returnvalue (if number is inside boundaries, nothing will happen)
    ret = round(arg);

    argsize = size(ret);

    for i = 1:argsize(1)
        for j = 1:argsize(2)
            ret(i,j) = flow_sg(ret(i,j),bw);
        end
    end

end%function

%---

function ret = flow_sg(arg,bw)
    ret = arg;
    while (ret < (-2^(bw-1)))
        ret = ret + 2^bw;
    end

    % Check for overflows:
    while (ret > (2^(bw-1)-1))
        ret = ret - 2^bw;
    end
end%function
于 2010-03-11T14:04:15.847 に答える
1

オーバーフローしないのに 64 ビットで十分であり、これらがたくさん必要な場合は、おそらく次のようにします。

function ret = overflow_sg(arg,bw)
  mask = int64(0);
  for i=1:round(bw)
    mask = bitset(mask,i);
  end
  topbit = bitshift(int64(1),round(bw-1));
  subfrom = double(bitshift(topbit,1))


  ret = bitand( int64(arg) , mask );
  i = (ret >= topbit);
  ret(i) = int64(double(ret(i))-subfrom);
  if (bw<=32)
    ret = int32(ret);
  end
end

ほとんどすべてが行列計算として行われ、多くはビットで行われ、すべてが 1 つのステップ (while ループなし) で行われるため、かなり高速になるはずです。これに rand を設定する場合は、0.5 を引きます。これは、(切り捨てではなく) 整数値に丸める必要があると想定しているためです。

于 2010-03-11T22:11:46.443 に答える