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?

14
demandé sur gnovice 2010-03-11 16:32:06

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. Type help 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
18
répondu gnovice 2015-11-10 14:10:01

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.

5
répondu Andrew Janke 2010-03-15 19:01:42

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

1
répondu High Performance Mark 2010-03-11 13:51:07

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
1
répondu marvin2k 2010-03-11 14:04:15

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

1
répondu Rex Kerr 2010-03-11 22:11:46

regardez intwarning fonction.

0
répondu AVB 2010-03-11 14:47:05

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

0
répondu Scott C 2017-01-09 17:27:00