2010-03-11 12 views
14

Estoy trabajando en una herramienta de verificación para algunos códigos VHDL en MATLAB/Octave. Por lo tanto necesito tipos de datos que generan desbordamientos "reales":¿Cómo obtengo desbordamientos de enteros reales en MATLAB/Octave?

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

Más adelante, sería de gran ayuda si puedo definir el número de bits de una variable, pero eso no es tan importante en este momento.

Cuando construyo un ejemplo parecido a C, donde una variable se incrementa hasta que es menor que cero, que gira siempre y para siempre:

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

Otro enfoque probé fue una -routine personalizada "desbordamiento", que fue llamado cada vez que se cambia un número. Este enfoque fue dolorosamente lento, no practicable y no funciona en todos los casos. ¿Alguna sugerencia?

Respuesta

18

En MATLAB, una opción que tiene es sobrecargar los métodos que manejan arithmetic operations para integer data types, creando su propio comportamiento de desbordamiento personalizado que dará como resultado un "envolvimiento" del valor entero.Como se indica en la documentación:

Puede definir o sobrecargar sus propios métodos para int* (como pueda para cualquier objeto ) colocando el método apropiadamente nombrado en una carpeta @int* dentro una carpeta en su camino . Escriba help datatypes para obtener los nombres de los métodos que puede sobrecargar.

This page of the documentation enumera los métodos equivalentes para los operadores aritméticos. La operación de adición binaria A+B es realmente manejada por la función plus(A,B). Por lo tanto, puede crear una carpeta llamada @int32 (colocada en otra carpeta en su MATLAB path) y poner una función plus.m allí que se usará en lugar del método incorporado para los tipos de datos int32.

Aquí hay un ejemplo de cómo se puede diseñar su plus función sobrecargada con el fin de crear el comportamiento de desbordamiento/subdesbordamiento desea:

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 

en cuenta que yo llamo la incorporada en plus método (usando la función BUILTIN) para realizar la adición de int32 valores que sé que no sufrirán problemas de desbordamiento/subdesbordamiento. Si fuera a realizar la suma entera utilizando la operación A+B, se produciría una llamada recursiva a mi método plus sobrecargado, lo que podría generar una sobrecarga computacional adicional o (en el peor de los casos, la última línea fue C = A+B;) recursión infinita .

Aquí está una prueba, que muestra el-torno envoltura comportamiento de desbordamiento en acción:

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

+1 para dar con la sobrecarga de operadores! – Jonas

+4

+1 Eficaz pero aterrador. Creo que esto debería venir con la advertencia de que reemplazar operadores en tipos estándar podría interactuar mal con otras bibliotecas o funciones de Matlab que están esperando el comportamiento "+" normal de Matlab en ellas. (Incluso si MathWorks lo recomienda en el doco.) Potente para soluciones únicas, problemáticas para grandes bases de código. –

+1

@Andrew: Haces un buen punto. Imagino que este tipo de solución solo se usa para la herramienta específica que está creando el OP, pero podría ser fácil olvidarse de volver al antiguo método 'int32' una vez que la herramienta haya terminado de ejecutarse. Para usar esto solo para un fragmento específico de código, tendría un comando al principio de ese código que agregaba la carpeta principal de la carpeta '@ int32' a la ruta, y luego la eliminaba de la ruta cuando terminaba de ejecutarse. – gnovice

1

Me corrió el siguiente fragmento de código

test = int32(2^31-12); 
for i = 1:24 
    test = test + int32(1) 
end 

con resultados inesperados. Parece que, para Matlab, intmax('int32')+1==intmax('int32'). Estoy ejecutando 2010a en un Mac OS X de 64 bits.

No estoy seguro de que esto sea una respuesta, más una confirmación de que Matlab se comporta contra intuitivamente. Sin embargo, la documentación para la función intmax() indica:

Cualquier valor mayor que el valor devuelto por intmax se satura al valor intmax cuando se lo convierte en un entero de 32 bits.

Supongo que Matlab se comporta como se documenta.

1

Hm, sí ...

En realidad, yo era capaz de resolver el problema con mi costumbre "desbordamiento" -Subroutine ... Ahora se ejecuta muy lento, pero sin un comportamiento inesperado! Mi error fue una ronda faltante(), ya que Matlab/Octave presentará pequeños errores.

Pero si alguien conoce una solución más rápida, ¡me encantaría probarla!

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 
0

Eche un vistazo a la función intwarning.

1

If 64 bits es suficiente como para no desbordarse, y se necesita una gran cantidad de estos, tal vez hacer esto:

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 

Casi todo se hace como un cálculo de matriz, y mucho se realiza mediante bits, y todo se hace en un solo paso (no while bucles), por lo que debe ser bastante rápido. Si vas a poblarlo con rand, reste 0.5 dado que asume que debe redondear a valores enteros (en lugar de truncar).

5

Si desea obtener operaciones numéricas estilo C, puede utilizar una función MEX a llamar directamente a los operadores de C, y por definición que funcionarán como tipos de datos C.

Este método es una mucho más trabajo que las anulaciones de gnovice, pero debe integrarse mejor en una gran base de código y es más seguro que la alteración de la definición de los tipos predefinidos, así que creo que debe ser mencionado para la integridad.

Aquí hay un archivo MEX que realiza la operación C "+" en una matriz de Matlab. Haga uno de estos para cada operador en el que desee el comportamiento estilo 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; 
} 

Luego tiene que averiguar cómo invocarlo desde su código de Matlab. Si está escribiendo todo el código, puede simplemente llamar "c_plus (a, b)" en lugar de "a + b" en todas partes. Alternativamente, puede crear su propia clase contenedora numérica, p. @cnumeric, que contiene una matriz numérica Matlab en su campo y define más() y otras operaciones que invocan la función MEX de estilo C apropiada.

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 

Luego ajuste sus números en @cnumeric donde desea el comportamiento int de estilo C y haga cálculos matemáticos con ellos.

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

Tiene su comportamiento de desbordamiento estilo C, aislado de romper el tipo primitivo @ int32.Además, puede pasar un objeto @cnumeric a otras funciones que esperan números regulares y "funcionará" siempre que traten sus entradas de forma polimórfica.

Advertencia de rendimiento: como se trata de un objeto, + tendrá la velocidad más lenta de un envío de método en lugar de una función incorporada. Si tiene pocas llamadas en arreglos grandes, esto será rápido, porque las operaciones numéricas reales están en C. Muchas llamadas en arreglos pequeños, podrían ralentizar las cosas, porque está pagando mucho por la sobrecarga por llamada al método.

+0

+1: Bastante más trabajo, pero una buena idea. Supongo que incluso otra solución sería tomar los métodos sobrecargados de mi solución y simplemente crear un nuevo objeto definido por el usuario usando esos métodos (como su solución, pero sin los archivos MEX). – gnovice

0

No soy un experto en Java, pero las clases subyacentes de Java disponibles en Matlab deben permitir el manejo de desbordamientos como C. Una solución que encontré, funciona solo para un valor único, pero convierte un número a la representación int16 (Corto) o int32 (Entero). Debe hacer sus cálculos usando el doble de Matlab, luego convierta a Java int16 o int32, y luego conviértalo nuevamente a Matlab doble. Desafortunadamente, Java no parece admitir tipos sin firmar de esta manera, solo firmado.

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

Cuestiones relacionadas