Этот пример показывает, как выполнить основные арифметические операции с фиксированной точкой.
Сохраните состояния предупреждения перед началом.
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
, а 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
с тем же 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
Обратите внимание, что в C результат операции между целочисленным типом данных и двойным типом данных способствует двойному.
Однако в MATLAB результатом операции между встроенным целочисленным типом данных и двойным типом данных является целое число. В этом отношении fi
объект ведет себя как встроенные целочисленные типы данных в MATLAB. Результат операции между fi
и double является fi
.
При выполнении арифметики между 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>