14

我正在为 MATLAB/Octave 中的一些 VHDL 代码开发验证工具。因此我需要产生“真实”溢出的数据类型:

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

稍后,如果我可以定义变量的位宽会很有帮助,但现在这并不重要。

当我构建一个类似 C 的示例时,变量会增加直到小于零,它会永远旋转:

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)。因此,您可以创建一个名为@int32(放置在您的MATLAB 路径上的另一个文件夹中)的文件夹,并在其中放置一个函数,该函数将用于代替数据类型plus.m的内置方法。int32

这是一个示例,说明如何设计重载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 的覆盖多得多,但它应该更好地集成到大型代码库中,并且比更改内置类型的定义更安全,所以我认为应该提到它的完整性。

这是一个 MEX 文件,它对 Matlab 数组执行 C“+”操作。为您希望 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 代码中调用它。如果您正在编写所有代码,则可以在任何地方都调用“c_plus(a, b)”而不是“a + b”。或者,您可以创建自己的数字包装类,例如@cnumeric,它在其字段中保存一个 Matlab 数字数组并定义 plus() 和其他调用适当的 C 样式 MEX 函数的操作。

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

有你的 C 风格的溢出行为,与破坏原始 @int32 类型隔离。另外,您可以将 @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'),. 我在 64 位 Mac OS X 上运行 2010a。

不确定这是一个答案,更多地确认 Matlab 的行为违反直觉。但是,该intmax()函数的文档指出:

当转换为 32 位整数时,任何大于 intmax 返回值的值都会饱和到 intmax 值。

所以我猜 Matlab 的行为与记录的一样。

于 2010-03-11T13:51:07.517 回答
1

嗯,是的……

实际上,我能够用我的自定义“溢出”子程序解决问题......现在它运行缓慢,但没有意外行为!我的错误是缺少 round(),因为 Matlab/Octave 会引入小错误。

但如果有人知道更快的解决方案,我很乐意尝试!

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

几乎所有事情都是作为矩阵计算完成的,很多事情都是用位完成的,而且所有事情都是一步完成的(没有 while 循环),所以它应该很快。如果要使用 rand 填充它,请减去 0.5,因为它假定它应该舍入为整数值(而不是截断)。

于 2010-03-11T22:11:46.443 回答
0

我不是 Java 专家,但 Matlab 中可用的底层 Java 类应该允许像 C 那样处理溢出。我发现的一种解决方案仅适用于单个值,但它将数字转换为 int16(短)或 int32(整数)表示。您必须使用 Matlab double 进行数学运算,然后转换为 Java int16 或 int32,然后再转换回 Matlab double。不幸的是,Java 似乎不以这种方式支持无符号类型,只支持有符号类型。

double(java.lang.Short(hex2dec('7FFF')))
<br>ans = 32767

double(java.lang.Short(hex2dec('7FFF')+1))
<br>ans = -32768

double(java.lang.Short(double(intmax('int16'))+1))
<br>ans = -32768

double(java.lang.Integer(hex2dec('7FFF')+1))
<br>ans = 32768

https://www.tutorialspoint.com/java/lang/java_lang_integer.htm

于 2017-01-09T15:56:37.667 回答