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

我正在为MATLAB / Octave中的一些VHDL代码validation工具。 因此我需要产生“真正”溢出的数据types:

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

稍后,如果我可以定义一个variables的位宽,这将是有帮助的,但是现在并不那么重要。

当我build立一个类似C的例子,variables增加到小于零时,它会永远旋转:

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

我尝试的另一种方法是一个自定义的“溢出” – 程序,每次在数字被改变后被调用。 这种做法非常缓慢,不切实际,根本不适用于所有情况。 有什么build议么?

在MATLAB中,您拥有的一个选项是重载处理整数数据types的 算术运算的方法,创build您自己的自定义溢出行为,这将导致整数值的“绕回”。 正如文件中所述:

您可以通过将适当命名的方法放置在path上的文件夹内的@int*文件夹中,来定义或重载自己的int*方法(就像任何对象一样)。 inputhelp datatypes作为您可以重载的方法的名称。

文档的这一页列出了算术运算符的等价方法。 二进制加法运算A+B实际上由函数plus(A,B) 。 因此,您可以创build一个名为@int32的文件夹(放在您的MATLABpath的另一个文件夹中),并在其中放置一个函数plus.m ,而不是使用int32数据types的内置方法。

下面是如何devise重载plusfunction以创build所需的上溢/下溢行为的示例:

 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 

注意,我调用了内build的加法(使用BUILTIN函数)来执行我知道不会遇到上溢/下溢问题的int32值的添加。 如果我要使用操作A+B来执行整数加法,则会导致对我的重载plus方法的recursion调用,这可能导致额外的计算开销,或者(在最后一行是C = A+B; )无限recursion。

这里是一个testing,显示了在行动中的环绕溢出行为:

 >> 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 

如果你想获得C风格的数字操作,你可以使用MEX函数直接调用C运算符,按照定义,它们将像C数据types一样工作。

这个方法比gnovice的覆盖更多,但是它应该更好地集成到一个大的代码库中,比修改内置types的定义更安全,所以我认为应该提到完整性。

这里是一个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”。 或者,您可以创build自己的数字包装类,例如@cnumeric,在其字段中包含一个Matlab数字数组,并定义调用适当的C样式MEX函数的plus()和其他操作。

 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(ax), class(bx)) error('inputs must have same wrapped type'); end out_x = c_plus(ax, bx); 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(ax - bx); 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(ax) && isa(bx, 'double') bx = cast(bx, class(ax)); end if isinteger(bx) && isa(ax, 'double') ax = cast(ax, class(bx)); end end end end 

然后把你的数字包装在你想要C风格的int行为的@cnumeric中,并且和他们做math运算。

 >> 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风格溢出行为,从打破原始@ int32types隔离。 另外,您可以将@cnumeric对象传递给其他正在使用常规数字的函数,只要它们以多态方式处理input,它就会“工作”。

性能告诫:因为这是一个对象,所以+将具有较慢的方法调度速度,而不是一个内build的。 如果你在大型数组上调用的次数很less,这将会很快,因为实际的数字操作是在C中进行的。很多小型数组的调用可能会让速度变慢,因为你要花费很多的方法调用开销。

我运行了下面的代码片段

 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()函数的文档状态如下:

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

所以我想Matlab的行为是logging。

嗯,是的

实际上,我可以用自定义的“溢出”来解决这个问题 – 子程序…现在它运行得非常缓慢,但没有意外的行为! 我的错误是一个缺席(),因为Matlab /八度会引入小错误。

但如果有人知道更快的解决scheme,我会很高兴尝试!

 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 

如果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 

几乎所有的事情都是以matrix计算完成的,很多事情都是通过位来完成的,一切都是一步完成的(没有while循环),所以它应该是相当快的。 如果你打算使用rand来填充它,则减去0.5,因为它假定它应该舍入为整数值(而不是截断)。

看看intwarning函数。

我不是一个Java专家,但Matlab中可用的基础Java类应该允许像C一样处理溢出。 我find的一个解决scheme,只适用于单个值,但它将数字转换为int16(Short)或int32(Integer)表示forms。 你必须使用Matlab double来做math计算,然后转换成Java int16或int32,然后再转换成Matlab double。 不幸的是Java似乎不支持这种无符号types,只有签名。

 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