Подтвердить что ты не робот

Как получить реальное целочисленное переполнение в MATLAB/Octave?

Я работаю над инструментом проверки некоторых VHDL-кодов в MATLAB/Octave. Поэтому мне нужны типы данных, которые генерируют "реальные" переполнения:

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

В дальнейшем было бы полезно, если бы я мог определить ширину бита переменной, но это не так важно прямо сейчас.

Когда я создаю C-образный пример, где переменная увеличивается до тех пор, пока она не станет меньше нуля, она будет вращаться во веки веков:

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

Другой подход, который я пробовал, - это обычная функция "переполнения", которая вызывалась каждый раз после изменения числа. Этот подход был болезненно медленным, неосуществимым и вообще не работал во всех случаях. Любые предложения?

4b9b3361

Ответ 1

В MATLAB один из вариантов - перегрузить методы, которые обрабатывают арифметические операции для целочисленные типы данных, создавая собственное пользовательское поведение переполнения, что приведет к "обертыванию" целочисленного значения. Как указано в документации:

Вы можете определить или перегрузить свои собственные методы для int* (как вы можете для любого объекта) путем размещения соответствующего named в папке @int* в пределах папку на вашем пути. Введите help datatypes для имен методов вы можете перегрузить.

На этой странице документации перечислены эквивалентные методы для арифметических операторов. Операция двоичного добавления A+B фактически обрабатывается функцией plus(A,B). Поэтому вы можете создать папку с именем @int32 (помещена в другую папку на путь MATLAB) и поместить туда функцию plus.m который будет использоваться вместо встроенного метода для типов данных 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

Обратите внимание, что я вызываю встроенный метод plus (используя функцию BUILTIN), чтобы выполнить добавление int32 значения, которые, как я знаю, не будут страдать от проблем с переполнением/недостаточным потоком. Если бы я вместо этого выполнял целочисленное добавление с помощью операции A+B, это привело бы к рекурсивному вызову моего перегруженного метода plus, что могло бы привести к дополнительным вычислительным издержкам или (в худшем случае, когда последняя строка была C = A+B;) бесконечная рекурсия.

Здесь показан тест, показывающий поведение переполнения обертывания в действии:

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

Ответ 2

Если вы хотите получить числовые операции в стиле C, вы можете использовать функцию MEX для прямого вызова операторов C, и по определению они будут работать как типы данных C.

Этот метод намного больше, чем переопределение гновиц, но он должен лучше интегрироваться в большую базу кода и безопаснее, чем изменять определение для встроенных типов, поэтому я думаю, что это нужно упомянуть для полноты.

Здесь MEX файл, который выполняет операцию C "+" в массиве Matlab. Сделайте один из них для каждого оператора, для которого вы хотите поведение стиля 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() и другие операции, которые вызывают подходящую функцию MEX в стиле C.

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

Затем заверните свои номера в @cnumeric, где вы хотите поведение стиля C-стиля и сделайте с ними математику.

>> 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. Множество вызовов на небольших массивах может замедлить работу, потому что вы платите за накладные расходы для каждого метода.

Ответ 3

Я выполнил следующий фрагмент кода

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

с неожиданными результатами. Кажется, что для Matlab intmax('int32')+1==intmax('int32'). Я запускаю 2010a на 64-разрядной Mac OS X.

Не уверен, что это как ответ, еще одно подтверждение того, что Matlab ведет себя противоречиво. Однако в документации для функции intmax() указано:

Любое значение, большее, чем значение, возвращаемое intmax, насыщается значением intmax при передаче в 32-разрядное целое.

Итак, я думаю, что Matlab ведет себя как задокументированный.

Ответ 4

Hm, да...

На самом деле, я смог решить проблему с моим пользовательским "переполнением" -Subroutine... Теперь он работает очень медленно, но без неожиданного поведения! Моя ошибка была отсутствующим раундом(), так как 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

Ответ 5

Если 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, так как он предполагает, что он должен округлять до целочисленных значений (а не обрезать).

Ответ 6

Взгляните на функцию intwarning.

Ответ 7

Я не эксперт по Java, но базовые классы Java, доступные в Matlab, должны позволять обрабатывать переполнения, такие как C. Одно найденное решение работает только для одного значения, но оно преобразует число в представление int16 (Short) или int32 (Integer). Вы должны сделать свою математику, используя Matlab double, а затем конвертировать в Java int16 или int32, а затем преобразовать обратно в Matlab double. К сожалению, Java, похоже, не поддерживает типы unsigned, только подписал.

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