Выполните арифметику с фиксированной точкой

Этот пример показывает, как выполнить основные арифметические операции с фиксированной точкой.

Сохраните состояния предупреждения перед началом.

warnstate = warning;

Сложение и вычитание

Всякий раз, когда вы добавляете два неподписанных номера с фиксированной точкой, вам может понадобиться бит переноса, чтобы правильно представлять результат. По этой причине при добавлении двух B-битных чисел (с тем же масштабированием) полученное значение имеет дополнительный бит по сравнению с двумя используемыми операндами.

a = ufi(0.234375,4,6);
c = a + a
c = 

    0.4688

          DataTypeMode: Fixed-point: binary point scaling
            Signedness: Unsigned
            WordLength: 5
        FractionLength: 6
a.bin
ans =

    '1111'

c.bin
ans =

    '11110'

С подписанными номерами двух комплементов аналогичный сценарий происходит из-за расширения знака, необходимого для правильного представления результата.

a = sfi(0.078125,4,6);
b = sfi(-0.125,4,6);
c = a + b
c = 

   -0.0469

          DataTypeMode: Fixed-point: binary point scaling
            Signedness: Signed
            WordLength: 5
        FractionLength: 6
a.bin
ans =

    '0101'

b.bin
ans =

    '1000'

c.bin
ans =

    '11101'

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

a = sfi(pi,16,13);
b = sfi(0.1,12,14);
c = a + b
c = 

    3.2416

          DataTypeMode: Fixed-point: binary point scaling
            Signedness: Signed
            WordLength: 18
        FractionLength: 14

Дополнительные факторы для сложения и вычитания

Обратите внимание, что следующий шаблон не рекомендуется. Поскольку скалярные сложения выполняются при каждой итерации в цикле for, бит добавляется к темпу во время каждой итерации. В результате вместо потолочного (log2 (Nadds)) роста разрядности, рост разрядности равен Наддсу.

s = rng; rng('default');
b = sfi(4*rand(16,1)-2,32,30);
rng(s); % restore RNG state
Nadds = length(b) - 1;
temp  = b(1);
for n = 1:Nadds
    temp = temp + b(n+1); % temp has 15 more bits than b
end

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

c = sum(b) % c has 4 more bits than b
c = 

    7.0059

          DataTypeMode: Fixed-point: binary point scaling
            Signedness: Signed
            WordLength: 36
        FractionLength: 30

Умножение

В целом полная точность продукта требует размера слова, равной сумме размеров слова операндов. В следующем примере обратите внимание, что размер слова продукта c равен размеру слова a плюс размер слова b. Длина дроби c также равно длине дроби a плюс длина дроби b.

a = sfi(pi,20);
b = sfi(exp(1),16);
c = a * b
c = 

    8.5397

          DataTypeMode: Fixed-point: binary point scaling
            Signedness: Signed
            WordLength: 36
        FractionLength: 30

Назначение

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

N = 10;
a = sfi(2*rand(N,1)-1,16,15);
b = sfi(2*rand(N,1)-1,16,15);
c = sfi(zeros(N,1),16,14);
for n = 1:N
    c(n) = a(n).*b(n);
end

Обратите внимание, что когда продукт a(n).*b(n) вычисляется с полной точностью, генерируется промежуточный результат с длиной слова 32 и длиной фракции 30. Этот результат затем квантуется до длины слова 16 и длины фракции 14, как объяснено выше. Квантованное значение затем присваивается элементу c(n).

Явное квантование результатов

Часто нежелательно округлять до ближайшего или насыщать при квантовании результата из-за дополнительной логики/вычислений, необходимых. Также может быть нежелательно, чтобы для выполнения квантования было присвоено значение с левой стороны. Можно использовать QUANTIZE для таких целей. Распространенным случаем является цикл обратной связи. Если квантование не введено, будет происходить неограниченный рост разрядности по мере предоставления большего количества входных данных.

a = sfi(0.1,16,18);
x = sfi(2*rand(128,1)-1,16,15);
y = sfi(zeros(size(x)),16,14);
for n = 1:length(x)
    z    = y(n);
    y(n) = x(n) - quantize(a.*z, true, 16, 14, 'Floor', 'Wrap');
end

В этом примере продукт a.*z вычисляется с полной точностью и затем квантуется до wordlength 16 бит и длины дроби 14. Квантование выполняется путем округления до пола (усечение) и обеспечения переноса, если происходит переполнение. Квантование все еще происходит при назначении, потому что выражение x(n) - quantize(a.*z, ...) выдает промежуточный результат 18 бит, и y определяется как имеющее 16 биты. Чтобы исключить квантование при назначении, можно ввести дополнительное явное квантование, как показано ниже. Преимущество этого состоит в том, что не используется логика «круглый к ближайшему/насыщению». Результат с левой стороны имеет ту же 16-битную словарную длину и длину дроби 14, что и y(n), поэтому квантование не требуется.

a = sfi(0.1,16,18);
x = sfi(2*rand(128,1)-1,16,15);
y = sfi(zeros(size(x)),16,14);
T = numerictype(true, 16, 14);
for n = 1:length(x)
    z    = y(n);
    y(n) = quantize(x(n), T, 'Floor', 'Wrap') - ...
           quantize(a.*z, T, 'Floor', 'Wrap');
end

Суммы без полной точности

Полностью точные суммы не всегда желательны. Для примера 18-битная длина слова, соответствующая промежуточному результату x(n) - quantize(...) вышесказанное может привести к сложному и неэффективному коду, если Код С сгенерирован. Вместо этого может быть желательно сохранить все результаты сложения/вычитания в 16 битах. Можно использовать accumpos и accumneg функций для этой цели.

a = sfi(0.1,16,18);
x = sfi(2*rand(128,1)-1,16,15);
y = sfi(zeros(size(x)),16,14);
T = numerictype(true, 16, 14);
for n = 1:length(x)
    z    = y(n);
    y(n) = quantize(x(n), T);                 % defaults: 'Floor','Wrap'
    y(n) = accumneg(y(n), quantize(a.*z, T)); % defaults: 'Floor','Wrap'
end

Моделирование аккумуляторов

accumpos и accumneg хорошо подходят для моделирования аккумуляторов. Поведение соответствует операторам + = и - = в С. Общим примером является КИХ-фильтр, в котором коэффициенты и входные данные представлены 16 битами. Умножение выполняется с полной точностью, получая 32 бита, и аккумулятор с 8 защитными битами, т.е. 40 битами всего используется для обеспечения накопления до 256 накоплений без возможности переполнения.

b = sfi(1/256*[1:128,128:-1:1],16); % Filter coefficients
x = sfi(2*rand(300,1)-1,16,15);     % Input data
z = sfi(zeros(256,1),16,15);        % Used to store the states
y = sfi(zeros(size(x)),40,31);      % Initialize Output data
for n = 1:length(x)
    acc = sfi(0,40,31); % Reset accumulator
    z(1) = x(n);        % Load input sample
    for k = 1:length(b)
        acc = accumpos(acc,b(k).*z(k)); % Multiply and accumulate
    end
    z(2:end) = z(1:end-1); % Update states
    y(n) = acc;            % Assign output
end

Матричная арифметика

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

z = sfi(zeros(256,1),16,15); % Used to store the states
y = sfi(zeros(size(x)),40,31);
for n = 1:length(x)
    z(1) = x(n);
    y(n) = b*z;
    z(2:end) = z(1:end-1);
end

Область скалярного произведения b*z выполняется с полной точностью. Поскольку это матричная операция, рост разрядности обусловлен как задействованным умножением, так и сложением получившихся продуктов. Поэтому рост разрядности зависит от длины операндов. Начиная с b и z имеют длину 256, которая учитывает 8-битный рост из-за сложений. Вот почему скалярное произведение приводит к 32 + 8 = 40 битам (с длиной дроби 31). Поскольку это формат y инициализируется в, квантование не происходит в назначении y(n) = b*z.

Если бы вам пришлось выполнить скалярное произведение для более чем 256 коэффициентов, рост разрядности был бы более чем на 8 биты выше 32 необходимых для продукта. Если бы у вас был только 40-битный аккумулятор, вы могли бы смоделировать поведение, введя квантователь, как в y(n) = quantize(Q,b*z), или вы могли бы использовать accumpos функция, как показано.

Моделирование счетчика

accumpos может использоваться, чтобы смоделировать простой счетчик, который естественно оборачивается после достижения его максимального значения. Для примера можно смоделировать 3-битный счетчик следующим образом.

c = ufi(0,3,0);
Ncounts = 20; % Number of times to count
for n = 1:Ncounts
    c = accumpos(c,1);
end

Поскольку 3-битный счетчик естественно оборачивается назад к 0 после достижения 7, конечное значение счетчика является mod (20,8) = 4.

Математика с другими встроенными типами данных

FI * DOUBLE

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

a = fi(pi);
b = 0.5 * a
b = 

    1.5708

          DataTypeMode: Fixed-point: binary point scaling
            Signedness: Signed
            WordLength: 32
        FractionLength: 28

FI + DOUBLE или FI - DOUBLE

При выполнении сложения или вычитания между fi и double, двойник приведен к fi с тем же numerictype как fi. Результатом операции является fi.

Такое поведение fi + double изменен в R2012b. Отключить предупреждение о несовместимости можно путем ввода следующей команды предупреждения.

warning off fixed:incompatibility:fi:behaviorChangeHeterogeneousMathOperationRules
a = fi(pi);
b = a + 1
b = 

    4.1416

          DataTypeMode: Fixed-point: binary point scaling
            Signedness: Signed
            WordLength: 17
        FractionLength: 13

Некоторые различия между MATLAB ® и

C

Обратите внимание, что в C результат операции между целочисленным типом данных и двойным типом данных способствует двойному.

Однако в MATLAB результатом операции между встроенным целочисленным типом данных и двойным типом данных является целое число. В этом отношении fi объект ведет себя как встроенные целочисленные типы данных в MATLAB. Результат операции между fi и double является fi.

FI * INT8

При выполнении арифметики между fi и одним из встроенных целочисленных типов данных [u] int [8,16,32] сохраняются длина слова и сигнальность целого числа. Результатом операции является fi.

a = fi(pi);
b = int8(2) * a
b = 

    6.2832

          DataTypeMode: Fixed-point: binary point scaling
            Signedness: Signed
            WordLength: 24
        FractionLength: 13

Восстановите предупреждающие состояния.

warning(warnstate);
%#ok<*NASGU,*NOPTS>