Main Content

Questa pagina si riferisce alla release precedente. La corrispondente pagina in inglese è stata rimossa nella release attuale.

Esecuzione aritmetica in virgola fissa

Questo esempio mostra come eseguire operazioni aritmetiche di base in virgola fissa.

Salvare gli stati di avviso prima di iniziare.

warnstate = warning;

Addizione e sottrazione

Ogni volta che si sommano due numeri a virgola fissa senza firma, potrebbe essere necessario un bit di riporto per rappresentare correttamente il risultato. Per questo motivo, quando si sommano due numeri B-bit (con lo stesso ridimensionamento), il valore risultante ha un bit in più rispetto ai due operandi utilizzati.

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'

Con i numeri in complemento a due firmati, si verifica uno scenario simile a causa dell'estensione di segno necessaria per rappresentare correttamente il risultato.

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'

Se si sommano o si sottraggono due numeri con precisione diversa, è necessario prima allineare il punto radice per eseguire l’operazione. Il risultato è che sarà presente una differenza di più di un bit tra il risultato dell’operazione e gli operandi (a seconda della distanza tra i punti radice).

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

Ulteriori considerazioni sull’addizione e sulla sottrazione

Si noti che il seguente schema non è consigliato. Poiché le addizioni scalari vengono eseguite ad ogni iterazione del for-loop, viene aggiunto un bit temporaneo durante ogni iterazione. Di conseguenza, invece di una crescita di bit ceil(log2(Nadds)), la crescita di bit è uguale a 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

Se invece si utilizza il comando sum, la crescita di bit è limitata come previsto.

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

Moltiplicazione

In generale, un prodotto di massima precisione richiede una lunghezza della parola uguale alla somma delle lunghezze delle parole degli operandi. Nell’esempio seguente, si noti che la lunghezza della parola del prodotto c è uguale alla lunghezza della parola di a più la lunghezza della parola di b. La lunghezza della frazione di c è anche uguale alla lunghezza della frazione di a più la lunghezza della frazione di 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

Assegnazione

Quando si assegna un valore in virgola fissa a una variabile predefinita, potrebbe essere coinvolta la quantizzazione. In questi casi, il lato destro dell’espressione è quantizzato arrotondandolo al più vicino e quindi saturando, se necessario, prima di assegnarlo al lato sinistro.

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

Si noti che quando il prodotto a(n).*b(n) è calcolato con la massima precisione, si genera un risultato intermedio con lunghezza della parola di 32 bit e lunghezza della frazione di 30. Come spiegato sopra, tale risultato viene quindi quantizzato a una lunghezza della parola di 16 e a una lunghezza della frazione di 14. Il valore quantizzato viene quindi assegnato all’elemento c(n).

Quantizzazione dei risultati in modo esplicito

Spesso, non è desiderabile arrotondare al più vicino o saturare quando si quantizza un risultato a causa della logica/del calcolo extra richiesti, così come potrebbe non essere desiderabile dover assegnare un valore al lato sinistro per eseguire la quantizzazione. Per tali scopi, è possibile utilizzare QUANTIZE. Un caso comune è un loop di feedback. Se non viene introdotta nessuna quantizzazione, si verificherà una crescita di bit illimitata man mano che vengono forniti più dati di input.

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

In questo esempio, il prodotto a.*z è calcolato con la massima precisione e successivamente quantizzato a una lunghezza della parola di 16 bit e a una lunghezza della frazione di 14. La quantizzazione viene eseguita arrotondando per difetto (troncamento) e consentendo l’avvolgimento in caso di overflow. La quantizzazione si verifica ancora al momento dell’assegnazione, poiché l’espressione x(n) - quantize(a.*z, ...) produce un risultato intermedio di 18 bit mentre y è definita per avere 16 bit. Per eliminare la quantizzazione all’assegnazione, è possibile introdurre un’ulteriore quantizzazione esplicita come illustrato di seguito. Il vantaggio di tale operazione è che non viene utilizzata la logica arrotondamento/saturazione. Il risultato sul lato sinistro ha la stessa lunghezza della parola di 16 bit e la lunghezza della frazione è di 14 come y(n); quindi, non è necessaria alcuna quantizzazione.

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

Somme di non massima precisione

Le somme di massima precisione non sempre sono desiderabili. Ad esempio, la lunghezza della parola di 18 bit corrispondente al risultato intermedio di x(n) - quantize(...) di cui sopra, può risultare in un codice complicato e inefficiente, qualora venga generato un codice C. Al contrario, potrebbe essere desiderabile mantenere tutti i risultati dell’addizione/sottrazione a 16 bit. Per tale scopo, è possibile utilizzare le funzioni accumpos e 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

Modellazione accumulatori

accumpos e accumneg sono adatti per modellare gli accumulatori. Il comportamento corrisponde agli operatori += e -= in C. Un esempio comune è il filtro FIR, in cui i coefficienti dei dati di input sono rappresentanti con 16 bit. La moltiplicazione viene eseguita con la massima precisione, ottenendo 32 bit e un accumulatore di 8 bit di guardia, ossia viene utilizzato un totale di 40 bit per consentire sino a 256 accumuli senza possibilità di overflow.

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

Aritmetica delle matrici

Per semplificare la sintassi e ridurre i tempi di simulazione, è possibile utilizzare l'aritmetica delle matrici. Per l'esempio del filtro FIR, è possibile sostituire il loop interno con un prodotto interno.

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

Il prodotto interno b*z viene eseguito con la massima precisione. Poiché si tratta di un'operazione di matrice, la crescita di bit è dovuta sia alla moltiplicazione coinvolta sia alla sommazione dei prodotti risultanti. Pertanto, la crescita di bit dipende dalla lunghezza degli operandi. La lunghezza di 256 di b e z rappresenta una crescita di 8 bit dovuta alle sommazioni. Questo è il motivo per cui il prodotto interno risulta essere 32 + 8 = 40 bit (con lunghezza della frazione 31). Poiché questo è il formato in cui y è inizializzata, non si verifica nessuna quantizzazione nell’assegnazione y(n) = b*z.

Se si dovesse eseguire un prodotto interno per più di 256 coefficienti, la crescita di bit sarebbe maggiore di oltre 8 bit, oltre i 32 necessari per il prodotto. Se si avesse un unico accumulatore a 40 bit, si potrebbe modellare il comportamento sia introducendo un quantizzatore, come in y(n) = quantize(Q,b*z), sia utilizzando la funzione accumpos, come illustrato sopra.

Modellazione di un contatore

accumpos può essere utilizzato per modellare un semplice contatore che avvolga naturalmente, dopo aver raggiunto il proprio valore massimo. Ad esempio, è possibile modellare un contatore a 3 bit come segue.

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

Poiché il contatore a 3 bit si riavvolge naturalmente a 0 dopo aver raggiunto 7, il valore finale del contatore è mod(20,8) = 4.

Matematica con altri tipi di dati integrati

FI * DOPPIO

Quando si esegue una moltiplicazione tra fi e double, il double viene convertito in un fi con la stessa lunghezza della parola e la stessa firma del fi e la lunghezza della frazione di miglior precisione. Il risultato dell’operazione è un fi.

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

    1.5708

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

FI + DOPPIO o FI - DOPPIO

Quando si esegue un’addizione o una sottrazione tra fi e double, il doppio viene convertito in un fi con lo stesso numerictype del fi. Il risultato dell’operazione è un fi.

Questo comportamento del fi + double è cambiato nella R2012b. È possibile disattivare l'avviso di incompatibilità immettendo il seguente comando di avviso.

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

Alcune differenze tra MATLAB® e C

Si noti che in C, il risultato di un’operazione tra un tipo di dati intero e un tipo di dati doppio viene promosso a doppio.

Tuttavia, in MATLAB, il risultato di un’operazione tra un tipo di dati intero integrato e un tipo di dati doppio è un numero intero. A questo proposito, l’oggetto fi si comporta come i tipi di dati interi integrati in MATLAB. Il risultato di un’operazione tra un fi e un doppio è un fi.

FI * INT8

Quando si esegue l’aritmetica tra fi e uno dei tipi di dati interi integrati [u]int[8,16,32], la lunghezza della parola e la firma dell’intero vengono preservate. Il risultato dell’operazione è un fi.

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

    6.2832

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

Ripristinare gli stati di avviso.

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