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

StackOverflow https://stackoverflow.com/questions/2425251

Вопрос

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

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

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

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

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

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

Это было полезно?

Решение

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

Вы можете определить или перегрузить свои собственные методы для int* (как вы можете для любого объекта), поместив соответствующим образом именованный метод в @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 метод (с помощью ВСТРОЕННЫЙ функция) для выполнения сложения 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

Другие советы

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

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

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

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

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 ведет себя так, как задокументировано.

Хм, да...

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

Если 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

Почти все делается в виде матричного расчета, многое делается с помощью битов, и все делается за один шаг (без циклов while), поэтому все должно быть довольно быстро.Если вы собираетесь заполнить его с помощью rand, вычтите 0,5, поскольку предполагается, что оно должно округляться до целых значений (а не усекаться).

Я не эксперт по Java, но базовые классы Java, доступные в Matlab, должны позволять обрабатывать переполнения, как это сделал бы 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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top