Comment obtenir de vrais débordements d'entiers dans MATLAB / Octave?
je travaille sur un outil de vérification pour un code VHDL dans MATLAB/Octave. Par conséquent, j'ai besoin de types de données qui génèrent des débordements "réels":
intmax('int32') + 1
ans = -2147483648
plus Tard, il serait utile si je peux définir les bits largeur d'une variable, mais qui n'est pas si important maintenant.
quand je construis un exemple de type C, où une variable est augmentée jusqu'à ce qu'elle soit plus petite que zéro, elle tourne pour toujours et à jamais:
test = int32(2^30);
while (test > 0)
test = test + int32(1);
end
une Autre approche que j'ai essayé était une coutume "débordement" -routine qui a été appelée à chaque fois qu'un numéro est changé. Cette approche était douloureusement lente, irréalisable et ne fonctionnait pas du tout dans tous les cas. Toutes les suggestions?
7 réponses
dans MATLAB, une option que vous avez est de surcharger les méthodes qui gèrent opérations arithmétiquestypes de données integer, créant votre propre comportement de débordement personnalisé qui résultera en un" wrap-around " de la valeur entière. Comme indiqué dans la documentation:
vous pouvez définir ou surcharger votre propre méthodes pour
int*
(que vous pouvez pour tout objet) en les plaçant de façon appropriée méthode nommée dans un@int*
dossier à l'intérieur de dossier sur votre chemin. Typehelp datatypes
pour les noms de méthodes vous pouvez surcharge.
cette page de la documentation liste les méthodes équivalentes pour les opérateurs arithmétiques. L'opération d'addition binaire A+B
est en fait géré par la fonction plus(A,B)
. Par conséquent, vous pouvez créer un dossier appelé @int32
(placé dans un autre dossier sur votre chemin MATLAB) et mettre une fonction plus.m
là qui sera utilisé à la place de l'intégré dans méthode pour int32
types de données.
voici un exemple de la façon dont vous pourriez concevoir votre surcharge plus
fonction afin de créer le comportement de débordement/sous-débit que vous voulez:
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
Remarquez que j'appelle le haut -plus
méthode (en utilisant la BUILTIN fonction) pour effectuer l'addition de int32
les valeurs dont je sais qu'elles ne souffriront pas de problèmes de débordement ou de sous-débordement. Si je devais plutôt effectuer l'addition entière en utilisant l'opération A+B
il serait suite à un appel récursif à mon surchargé C = A+B;) une récursion infinie.
voici un test, montrant le comportement de débordement enveloppant en action:
>> 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
si vous voulez obtenir des opérations numériques de style C, vous pouvez utiliser une fonction MEX pour appeler les opérateurs C directement, et par définition ils fonctionneront comme des types de données c.
Cette méthode est un beaucoup plus de travail que les overrides de gnovice, mais il devrait mieux s'intégrer dans une grande base de codes et est plus sûr que la modification de la définition pour les types intégrés, donc je pense qu'il devrait être mentionné pour l'exhaustivité.
voici un fichier MEX qui effectue L'opération C " + " sur un Tableau Matlab. Faites - en un pour chaque opérateur sur lequel vous voulez un comportement C-style.
/* 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;
}
alors vous devez trouver comment l'invoquer à partir de votre code Matlab. Si vous écrivez tout le code, vous pouvez simplement appeler "c_plus(a, b)" au lieu de "a + b" partout. Alternativement, vous pouvez créer votre propre classe de wrapper numérique, par exemple @cnumeric, qui détient un tableau numérique Matlab dans son champ et définit plus() et d'autres opérations qui invoquent la fonction MEX de style C appropriée.
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
ensuite, enveloppez vos nombres dans le @cnumeric où vous voulez un comportement int de style C et faites des maths avec eux.
>> 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
Voici votre comportement de débordement de style C, isolé de la rupture du type primitif @int32. De plus, vous pouvez passer un objet @cnumeric à d'autres fonctions qui attendent des nombres réguliers et cela "fonctionnera" aussi longtemps qu'ils traiteront leurs entrées polymorphiquement.
avertissement de Performance: parce que c'est un objet, + aura le plus lent la vitesse d'une méthode de répartition au lieu d'un builtin. Si vous avez peu d'appels sur les grands tableaux, ce sera rapide, parce que les opérations numériques réelles sont en C. Beaucoup d'appels sur les petits tableaux, pourrait ralentir les choses, parce que vous payez l'appel par méthode frais généraux beaucoup.
j'ai couru l'extrait de code suivant
test = int32(2^31-12);
for i = 1:24
test = test + int32(1)
end
avec des résultats inattendus. Il semble que, pour Matlab, intmax('int32')+1==intmax('int32')
. Je suis en cours d'exécution, 2010a sur une version 64 bits de Mac OS X.
pas sûr que ce soit une réponse, plus de confirmation que Matlab se comporte de manière contre-intuitive. Cependant, la documentation pour le intmax()
fonction:
toute valeur supérieure à la valeur retournée par intmax saturates à la valeur intmax lorsqu'elle est coulée à un 32 bits entier.
donc je suppose que Matlab se comporte comme documenté.
Hm, Oui...
en fait, j'ai pu résoudre le problème avec mon sous-programme personnalisé "overflow"... Maintenant il court péniblement lentement, mais sans comportement inattendu! Mon erreur a été un rond manquant (), puisque Matlab / Octave introduira de petites erreurs.
Mais si quelqu'un connait une solution plus rapide, je serais heureux de l'essayer!
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
si 64 bits est suffisant pour ne pas déborder, et vous avez besoin de beaucoup de ceux - ci, peut-être faire ceci:
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
presque tout est fait comme un calcul matriciel, et beaucoup est fait avec des bits, et tout est fait en une seule étape (Pas de boucles while), donc ça devrait être assez rapide. Si vous allez le peupler avec rand, soustrayez 0.5 car il suppose qu'il devrait rond à des valeurs entières (plutôt que de tronquer).
Je ne suis pas un expert de Java, mais les classes Java sous-jacentes disponibles dans Matlab devraient permettre de gérer les débordements comme le ferait C. Une solution que j'ai trouvée, ne fonctionne que pour une seule valeur, mais elle convertit un nombre à la représentation int16 (court) ou int32 (entier). Vous devez faire vos calculs en utilisant Matlab double, puis convertir en Java int16 ou int32, puis convertir de nouveau en Matlab double. Malheureusement, Java ne semble pas prendre en charge les types non signés de cette façon, seulement signer.
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