我正在为MATLAB / Octave中的一些VHDL代码开发验证工具。因此,我需要产生“真正”溢出的数据类型:
intmax('int32') + 1
ans = -2147483648
稍后,如果我可以定义变量的位宽,那将会很有帮助,但现在这不是很重要。
当我构建一个类似C的例子时,变量增加直到它小于零,它会永远旋转:
test = int32(2^30);
while (test > 0)
test = test + int32(1);
end
我尝试的另一种方法是自定义“溢出” - 例程,每次更改数字后都会调用它。这种方法非常缓慢,不实用,根本不适用于所有情况。有什么建议吗?
答案 0 :(得分:18)
在MATLAB中,你有一个选项是重载处理arithmetic operations的integer data types的方法,创建你自己的自定义溢出行为,这将导致整数值的“环绕”。如文档中所述:
您可以定义或超载自己的
int*
的方法(就像你可以做到的那样 对象)通过适当放置 在@int*
文件夹中的命名方法 路径上的文件夹。键入help datatypes
作为方法的名称 你可以超载。
This page of the documentation列出算术运算符的等效方法。二进制加法运算A+B
实际上由函数plus(A,B)
处理。因此,您可以创建一个名为@int32
的文件夹(放置在MATLAB path上的另一个文件夹中),并在其中放置一个函数plus.m
,而不是{的内置方法{ {1}}数据类型。
以下是一个示例,说明如何设计重载的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
方法(使用BUILTIN函数)来执行plus
值的添加,我知道这些值不会出现溢出/下溢问题。如果我使用操作int32
执行整数加法,则会导致对我的重载A+B
方法的递归调用,这可能导致额外的计算开销或(在最坏的情况下,最后一行是plus
)无限递归。
这是一个测试,显示了操作中的环绕溢出行为:
C = A+B;
答案 1 :(得分: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中。对小型数组的大量调用可能会减慢速度,因为您需要大量支付每个方法的调用开销。 / p>
答案 2 :(得分: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()
函数的文档说明:
任何大于intmax返回的值的值在转换为32位整数时都会饱和到intmax值。
所以我猜Matlab的行为就像记录在案一样。
答案 3 :(得分: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
答案 4 :(得分: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
几乎所有的东西都是以矩阵计算的方式完成的,而且很多东西是用比特来完成的,而且一切都是在一步完成的(没有循环),所以它应该非常快。如果你打算用rand填充它,减去0.5,因为它假定它应该舍入到整数值(而不是截断)。
答案 5 :(得分:0)
查看intwarning
功能。
答案 6 :(得分: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