В этом примере показано, как выполнять основные арифметические операции с фиксированной точкой.
Перед началом сохраните состояния предупреждения.
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-цикле, бит добавляется в temp во время каждой итерации. В результате вместо ceil (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 вычисляется с полной точностью и затем квантуется до длины слова 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 хорошо подходят для моделирования аккумуляторов. Поведение соответствует операторам + = и - = в C. Общим примером является фильтр FIR, в котором коэффициенты и входные данные представлены 16 битами. Умножение выполняется с полной точностью, давая 32 бита, а для возможности до 256 накоплений без возможности переполнения используется накопитель с 8 защитными битами, то есть 40-битный итог.
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
Для упрощения синтаксиса и сокращения времени моделирования можно использовать матричную арифметику. В примере фильтра FIR внутренний контур можно заменить внутренним продуктом.
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 и двойник является 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>