Cette page s'applique à la version précédente. La page correspondante en anglais a été supprimée de la version actuelle.
Effectuer des opérations en arithmétique à virgule fixe
Cet exemple montre comment effectuer les opérations de base en arithmétique à virgule fixe.
Sauvegardez les états d’avertissement (warning states) avant de commencer.
warnstate = warning;
Addition et soustraction
Chaque fois que vous additionnez deux nombres à virgule fixe non signés, il est possible que vous ayez besoin d’un bit de report pour représenter correctement le résultat. Pour cette raison, lors de l’addition de deux nombres à B bits (avec la même mise à l’échelle), la valeur résultante possède un bit supplémentaire par rapport aux deux opérandes utilisés.
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'
Avec les nombres en complément à deux signés, un scénario similaire se produit en raison de l'extension de signe nécessaire pour représenter correctement le résultat.
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'
Si vous additionnez ou soustrayez deux nombres d’une précision différente, il faut d’abord aligner la séparation fractionnaire pour pouvoir effectuer l’opération. Le résultat est qu’il y a une différence de plus d’un bit entre le résultat de l’opération et les opérandes (en fonction de la distance qui sépare les séparations fractionnaires).
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
Autres considérations sur l’addition et la soustraction
Remarque : le modèle de code suivant n’est pas recommandé. Comme les additions scalaires sont effectuées à chaque itération dans la boucle for, un bit est ajouté à la variable temp lors de chaque itération. Il en résulte qu’au lieu d’une croissance de bits de ceil(log2(Nadds)), la croissance des bits est égale à 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
Si, à la place, on utilise la commande sum
, la croissance des bits est freinée comme attendu.
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
Multiplication
Généralement un produit en précision totale nécessite une longueur de mot égale à la somme des longueurs de mot des opérandes. Dans l’exemple suivant, notez que la longueur de mot du produit c
est égale à la longueur de mot de a
plus la longueur de mot de b
. La longueur de la partie fractionnaire de c
est aussi égale à la longueur de la partie fractionnaire de a
plus la longueur de la partie fractionnaire de 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
Affectation
Lorsque vous affectez une valeur à virgule fixe dans une variable prédéfinie, cela peut impliquer une quantification. Dans de tels cas, la partie droite de l’expression est quantifiée en arrondissant au plus proche puis en saturant, si nécessaire, avant d’affecter à la partie gauche.
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
Notez que, lorsque le produit a(n).*b(n)
est calculé avec une précision totale, cela génère un résultat intermédiaire de longueur de mot 32 et de longueur de partie fractionnaire 30. Ce résultat est alors quantifié à une longueur de mot de 16 et une longueur de partie fractionnaire de 14, comme expliqué précédemment. La valeur quantifiée est ensuite affectée à l’élément c(n)
.
Quantification explicite des résultats
Souvent, il n’est pas souhaitable d’arrondir au plus proche ou de saturer lors de la quantification d’un résultat car cela entraîne de la logique/du calcul supplémentaire. Il n'est également pas souhaitable pour pouvoir effectuer la quantification d'avoir à affecter à une valeur côté gauche. Pour cela, vous pouvez utiliser QUANTIZE
. Un cas courant est celui de la boucle de rétroaction. Si on n’introduit pas de quantification, une croissance de bits illimitée va se produire à mesure que l’on fournira davantage de données d’entrée.
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
Dans cet exemple, le produit a.*z
est calculé avec une précision totale et est ensuite quantifié à une longueur de mot de 16 bits et une longueur de partie fractionnaire de 14. La quantification est réalisée en arrondissant au « plancher » (troncature) et en autorisant le wrapping en cas d'overflow. La quantification se produit malgré tout à l’affectation, car l’expression x(n) - quantize(a.*z, ...)
produit un résultat intermédiaire de 18 bits alors que y est défini pour avoir 16 bits. Pour éliminer la quantification à l’affectation, vous pouvez introduire une quantification explicite supplémentaire comme présenté ci-dessous. L’avantage de ce procédé est d’éviter de recourir à la logique de l’arrondi au plus proche/de la saturation. Le résultat côté gauche ayant la même longueur de mot de 16 bits et la même longueur de partie fractionnaire de 14 que y(n)
, il n’est pas nécessaire de quantifier.
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
Sommes à précision non totale
Il n’est pas toujours souhaitable d’effectuer des sommes à précision totale. Par exemple, la longueur de mot de 18 bits correspondant au résultat intermédiaire x(n) - quantize(...)
ci-dessus peut aboutir à un code compliqué et inefficace, si un code C est généré. Il peut être plus intéressant de maintenir tous les résultats d’addition/de soustraction à 16 bits. Pour ce faire, vous pouvez utiliser les fonctions accumpos
et 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
Modélisation des accumulateurs
accumpos
et accumneg
conviennent particulièrement à la modélisation des accumulateurs. Le comportement correspond à celui des opérateurs += et -= en C. Prenons l’exemple courant d’un filtre RIF dans lequel les coefficients et les données d’entrée sont représentés avec 16 bits. La multiplication s’effectue en précision totale, produisant 32 bits, et un accumulateur de 8 bits de garde, c’est-à-dire de 40 bits au total, est utilisé pour permettre jusqu’à 256 accumulations sans qu’il y ait possibilité d'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
Arithmétique matricielle
Pour simplifier la syntaxe et raccourcir les temps de simulation, vous pouvez utiliser l’arithmétique matricielle. Dans l’exemple du filtre RIF, vous pouvez remplacer la boucle interne par un produit interne.
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
Le produit interne b*z
est effectué en précision totale. Comme il s’agit d’une opération matricielle, la croissance des bits est due à la fois à la multiplication impliquée et à l’addition des produits résultants. C’est pourquoi la croissance des bits dépend de la longueur des opérandes. Étant donné que b
et z
ont une longueur de 256, cela représente une croissance de 8 bits due aux additions. C’est pour cela que le produit interne donne 32 + 8 = 40 bits (avec une longueur de la partie fractionnaire de 31). Puisqu’il s’agit du format dans lequel y
est initialisé, aucune quantification ne se produit pendant l'affectation y(n) = b*z
.
Si vous deviez effectuer un produit interne pour plus de 256 coefficients, la croissance des bits serait supérieure à 8 bits outre les 32 nécessaires pour le produit. Si vous n’aviez qu’un accumulateur 40 bits, vous pourriez modéliser le comportement soit en introduisant un quantificateur, comme dans y(n) = quantize(Q,b*z)
, soit en utilisant la fonction accumpos
comme montré précédemment.
Modélisation d’un compteur
accumpos
peut servir à modéliser un compteur simple qui « enveloppe » (wraps) naturellement après avoir atteint sa valeur maximale. Par exemple, vous pouvez modéliser un compteur sur 3 bits de la manière suivante.
c = ufi(0,3,0); Ncounts = 20; % Number of times to count for n = 1:Ncounts c = accumpos(c,1); end
Puisque le compteur sur 3 bits ramène naturellement par wrapping à 0 après avoir atteint 7, la valeur finale du compteur est mod(20,8) = 4.
Mathématiques avec d’autres types de données prédéfinis
FI * DOUBLE
Lors de la multiplication entre fi
et double
, le double
est converti (par cast) en un fi
ayant la même longueur de mot et le même signe que le fi
, et une longueur de la partie fractionnaire dotée de la meilleure précision. Le résultat de l’opération est 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 + DOUBLE ou FI - DOUBLE
Lors de l’addition ou de la soustraction entre fi
et double
, le double est converti (par cast) en un fi
ayant la même propriété numerictype
que le fi
. Le résultat de l’opération est un fi
.
Ce comportement de fi
+ double
a été modifié dans la version R2012b. Il est possible d’inactiver l’avertissement d’incompatibilité en saisissant la commande d’avertissement suivante.
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
Quelques différences entre MATLAB® et C
Notez qu'en C, le résultat d’une opération entre un type de données entier et un type de données double donne un double.
Cependant, dans MATLAB, le résultat d’une opération entre un type de données entier prédéfini et un type de données double est un entier. À cet égard, l’objet fi
se comporte comme les types de données entiers prédéfinis dans MATLAB. Le résultat d’une opération entre un fi
et un double est un fi
.
FI * INT8
Lors d’opérations arithmétiques entre fi et l’un des types de données entiers prédéfinis [u]int[8,16,32], la longueur du mot et le signe de l’entier sont préservés. Le résultat de l’opération est un fi.
a = fi(pi); b = int8(2) * a
b = 6.2832 DataTypeMode: Fixed-point: binary point scaling Signedness: Signed WordLength: 24 FractionLength: 13
Restaurez les états d’avertissement (warning states).
warning(warnstate);
%#ok<*NASGU,*NOPTS>