如何在MATLAB/Octave中获得实数整数溢出?

mar*_*n2k 14 matlab overflow integer-overflow

我正在为MATLAB/Octave中的一些VHDL代码开发一个验证工具.因此,我需要生成"真正"溢出的数据类型:

intmax('int32') + 1
ans = -2147483648
Run Code Online (Sandbox Code Playgroud)

稍后,如果我可以定义变量的位宽,那将会很有帮助,但现在这不是那么重要.

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

test = int32(2^30);
while (test > 0)
    test = test + int32(1);
end
Run Code Online (Sandbox Code Playgroud)

我尝试的另一种方法是自定义"溢出" - 例程,每次更改数字后都会调用它.这种方法非常缓慢,不实用,根本不适用于所有情况.有什么建议?

gno*_*ice 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
Run Code Online (Sandbox Code Playgroud)

请注意,我调用内置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
Run Code Online (Sandbox Code Playgroud)

  • +1有效但可怕.我认为这应该有一个警告,即替换标准类型的运算符可能与其他库或Matlab函数交互不良,这些函数期望Matlab对它们执行正常的"+"行为.(即使MathWorks确实在doco中推荐它.)强大的快速一次性,对大型代码库有问题. (4认同)

And*_*nke 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;
}
Run Code Online (Sandbox Code Playgroud)

然后你必须弄清楚如何从你的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
Run Code Online (Sandbox Code Playgroud)

然后将您的数字包装在您希望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
Run Code Online (Sandbox Code Playgroud)

这是你的C风格的溢出行为,与打破原始的@ int32类型隔离开来.另外,你可以将@cnumeric对象传递给其他期望常规数字的函数,只要它们以多态方式处理它们的输入,它就会"工作".

性能警告:因为这是一个对象,+将具有较慢的方法调度速度而不是内置.如果您对大型数组的调用很少,那么这将是快速的,因为实际的数字操作在C中.对小型数组的大量调用可能会减慢速度,因为您需要大量支付每个方法的调用开销.