Outils d'utilisateurs

Outils du Site


virgule_fixe

Différences

Cette page vous donne les différences entre la révision choisie et la version actuelle de la page.

Lien vers cette vue

virgule_fixe [2015/06/30 16:09]
gbdivers
virgule_fixe [2016/07/05 18:53] (Version actuelle)
gbdivers
Ligne 1: Ligne 1:
  
-^ Chapitre précédent ^ [[programmez_avec_le_langage_c|Sommaire principal]] ^ Chapitre suivant ^+[[complex|Chapitre précédent]] ^ [[programmez_avec_le_langage_c|Sommaire principal]] ^ [[ratio|Chapitre suivant]] ^
  
-====== Les nombres à virgule fixe ======+====== [Aller plus loin] Les nombres à virgule fixe ======
  
 ===== Limitation des nombres réels ===== ===== Limitation des nombres réels =====
  
-Les nombres réels sont intéressants, puisqu'ils permettent de représenter des nombres plus grands que les nombres entiers. Si par exemple, vous essayez d’exécuter le code suivant, vous obtiendrez une erreur :+Les nombres réels sont intéressants, puisqu'ils permettent de représenter des nombres plus grands que les nombres entiers. Si par exemple, vous essayez d’exécuter le code suivant :
  
 <code cpp main.cpp> <code cpp main.cpp>
Ligne 16: Ligne 16:
 </code> </code>
  
-affiche (seules les premières erreurs sont copiées ici) :+vous obtiendrez une erreur (seules les premières erreurs sont copiées ici) :
  
 <code> <code>
Ligne 34: Ligne 34:
 La seconde erreur "ambiguous overload" indique que le compilateur ne sait pas comment afficher ce nombre (plus précisément, il ne sait pas quel opérateur ''<<'' utiliser avec ''std::cout'' pour afficher ce nombre). La seconde erreur "ambiguous overload" indique que le compilateur ne sait pas comment afficher ce nombre (plus précisément, il ne sait pas quel opérateur ''<<'' utiliser avec ''std::cout'' pour afficher ce nombre).
  
-Note : ce nombre n'a pas été choisit au hasard. Il s'agit du plus grand nombre représentable avec les types de base du C++, plus un. Vous verrez par la suite comment obtenir des informations sur les types, comme par exemple la valeur maximal possible.+Note : ce nombre n'a pas été choisit au hasard. Il s'agit du plus grand nombre représentable avec les types de base du C++, plus un. Vous verrez par la suite comment obtenir des informations sur les types, comme par exemple la valeur maximale possible.
  
-Si on modifie un tout petit peu ce code (en ajoutant une décimale), pour utiliser des nombres réels, le compilateur ne produit plus d'erreur :+Si on modifie un tout petit peu ce code (en ajoutant une décimale), pour utiliser un nombre réel, le compilateur ne produit plus d'erreur :
  
 <code cpp main.cpp> <code cpp main.cpp>
Ligne 58: Ligne 58:
 Une littérale est représentée par une valeur et un type. Le compilateur prend en compte les deux, pas uniquement la valeur.</note> Une littérale est représentée par une valeur et un type. Le compilateur prend en compte les deux, pas uniquement la valeur.</note>
  
-Pour autant, les nombres réels ne sont pas parfaits non plus (sinon, on ne s’embêterait pas faire la distinction entre entiers et réels). Prenons un autre code d'exemple :+Pour autant, les nombres réels ne sont pas parfaits non plus (sinon, on ne s’embêterait pas à faire la distinction entre entiers et réels). Prenons un autre code d'exemple :
  
 <code cpp main.cpp> <code cpp main.cpp>
Ligne 76: Ligne 76:
 </code> </code>
  
-On a donc deux nombres, représentées dans le premier cas par des entiers et dans le second par des réels (notez la décimale). Ces deux nombres sont grands, mais peuvent être représentée sans problème par des entiers en C++ (plus précisément par le type ''long long int'', que vous verrez ensuite). La différence entre ces deux nombres vaut un.+On a donc deux nombres, représentées dans le premier cas par des entiers et dans le second par des réels (notez la décimale). Ces deux nombres sont grands, mais peuvent être représentés sans problème par des entiers en C++ (plus précisément par le type ''long long int'', que vous verrez ensuite). La différence entre ces deux nombres vaut un.
  
 Dans tous les cas, les nombres en C++ sont représentés par un nombre fini d'octets dans la mémoire des ordinateurs. Il n'est donc pas possible de représenter tous les nombres possibles (ce qui n'aurait de toute façon aucun sens, puisque qu'il existe une infinité de nombres réels). Les nombres réels peuvent représenter des nombres plus grands que les nombres entiers parce qu'ils ne peuvent pas représenter les grands nombres avec la même précision que les nombres entiers. Dans tous les cas, les nombres en C++ sont représentés par un nombre fini d'octets dans la mémoire des ordinateurs. Il n'est donc pas possible de représenter tous les nombres possibles (ce qui n'aurait de toute façon aucun sens, puisque qu'il existe une infinité de nombres réels). Les nombres réels peuvent représenter des nombres plus grands que les nombres entiers parce qu'ils ne peuvent pas représenter les grands nombres avec la même précision que les nombres entiers.
Ligne 82: Ligne 82:
 Dans le code d'exemple précédent, les nombres entiers ne sont pas arrondis et le calcul est juste. Au contraire, les nombres réels sont arrondis et sont représentées par la même valeur en mémoire. Le calcul se fait donc sur la même valeur et le résultat est nul. Dans le code d'exemple précédent, les nombres entiers ne sont pas arrondis et le calcul est juste. Au contraire, les nombres réels sont arrondis et sont représentées par la même valeur en mémoire. Le calcul se fait donc sur la même valeur et le résultat est nul.
  
-<note>Encore une fois, il est important d'insister la dessus : les types ont une grande importance en C++. Le choix du type peut modifier complètement le résultat d'un calcul. Faites bien attention cela, c'est une erreur qui revient souvent.</code>+<note>Encore une fois, il est important d'insister là-dessus : les types ont une grande importance en C++. Le choix du type peut modifier complètement le résultat d'un calcul. Faites bien attention à cela, c'est une erreur qui revient souvent.</note>
  
-====== Principe des nombres à virgule fixe ======+===== Principe et arithmétique =====
  
 Imaginons que vous souhaitez travailler sur des nombres décimaux, mais en conservant la précision des nombres entiers (vous ne voulez pas que les valeurs soient arrondies). Par exemple, dans une application bancaire, qui manipule des centimes (deux chiffres après la virgule).  Imaginons que vous souhaitez travailler sur des nombres décimaux, mais en conservant la précision des nombres entiers (vous ne voulez pas que les valeurs soient arrondies). Par exemple, dans une application bancaire, qui manipule des centimes (deux chiffres après la virgule). 
  
 +Le principe des nombres à virgule fixe est relativement simple : au lieu de manipuler des nombres réels, on va multiplier les valeurs par un facteur constant (généralement un multiple de 10) et utiliser cette représentation pour faire les calculs. C'est uniquement lors de l'affichage que l'on va recalculer la valeur décimale exacte.
  
 +Par exemple, dans le cas d'une application bancaire, on peut utiliser un facteur 100 :
  
 +<code>
 +  1    =   100
 + 12.34 =  1234
 +123.4  = 12340
 +</code>
  
 +En C++, il est possible de créer un nouveau type de nombre, qui permet de représenter plus facilement des nombres  à virgule fixe, mais vous verrez cela dans la partie sur la programmation objet. Dans ce chapitre, nous allons simplement utiliser un facteur directement dans le code. Ce n'est pas l’idéal en termes de conception, mais c'est suffisant pour le moment pour étudier le fonctionnement de cette représentation.
  
 +Définir une représentation n'est pas suffisante, il faut également définir les opérations arithmétiques de base. Pour l'addition et la soustraction, les opérateurs par défaut des entiers fonctionnent sans problème.
  
 +<code>
 +1.2 + 3.4 = 4.6
 +120 + 340 = 460 // facteur 100
 +</code>
  
 +Pour la multiplication et la division, il faut faire une correction : il faut diviser le résultat par le facteur pour la multiplication et multiplier par le facteur pour la division. Par exemple, pour la multiplication :
  
 +<code>
 +#include <iostream>
  
-Avec les nombres à virgule flottante, un problème de représentation de certain nombre avec virgule, qui ne sont pas représentable selon le type de réel__ donner un exemple __ Cette différence entre la valeur réelle et sa représentation dans un ordinateur peut être problématique.+int main() { 
 +    std::cout << (1.2 * 3.4) << std::endl; 
 +    std::cout << (120 * 340) / 100 << std::endl; 
 +
 +</code> 
 + 
 +affiche : 
 + 
 +<code> 
 +4.08 
 +408 
 +</code> 
 + 
 +Remarque : pour la division, le résultat en utilisant des nombres réels peut avoir plus de chiffres après la virgule que les opérandes. En utilisant les nombres à virgule fixeces décimales supplémentaires seront perdues. Encore une fois, on doit faire un compromis avec la précision. Si cet arrondi est problématique, il faudra utiliser un facteur plus important ou utiliser des nombres réels. 
 + 
 +**Exercice** : démontrer mathématiquement les propriétés précédentes pour la multiplication et la division. 
 + 
 +===== Facteurs non multiples de dix ===== 
 + 
 +Supposons que vous souhaitez compter de 0 à 10 avec un pas de un tiers (cela signifie que vous incrémentez votre compteur de 1/3 à chaque fois)Pour cela, vous allez écrire un code similaire au code suivant (en pseudo-code, pas en C++) : 
 + 
 +<code> 
 +compteur = compteur + 1.0 / 3.0; 
 +utiliser(compteur); 
 +</code> 
 + 
 +Remarque : un identifiant comme ''compteur'', qui permet de se souvenir d'une valeur, est appelée une variable. Vous verrez cela dans les prochains cours. 
 + 
 +Si vous répétez ce calcul, la différence entre la valeur calculée et la valeur correcte attendue va progressivement augmenter. Le graphique suivant représente l'erreur de calcul en fonction de la valeur du compteur : 
 + 
 +{{ :errors.png |}} 
 + 
 +Note : pour mieux voir le phénomène, j'ai volontairement utilisé des nombres réels sur 32 bits (simple précision). Il existe des types de nombres réels plus précis, mais ce n'est pas important ici. Le problème existe avec tous les types de nombres réels, la seule différence est qu'il met plus de temps à apparaître. 
 + 
 +Vous voyez que l'erreur devient assez vite importante. Selon vos besoins, vous pouvez accepter les valeurs en dessous de 1000, mais l'erreur devient trop importante au-delà. 
 + 
 +La solution est assez simple (vous l'aurez deviné, vu que l'on est dans le chapitre sur les nombres à virgule fixe) : il suffit d'utiliser un compteur entier, qui sera incrémenté de 1 à chaque fois. La valeur du compteur réel sera obtenue en divisant par 3. 
 + 
 +<code> 
 +compteur = compteur + 1; 
 +utiliser(compteur / 3.0); 
 +</code>
  
-De plusles calculs sur les nombres réels sont parfois plus longs que sur les nombres entiers.+Avec un compteur réel, si on compte jusqu'à 10 000, il y aura 30 000 additions réelles, chaque addition ajoutant une erreur (presque infime) de calcul. Ces erreurs vont s'accumulerjusqu'au point de ne plus être négligeables.
  
-La solution : mutliplier les nombres (pour les calculs) par une constante multiple de 10 et afficher le nombre réelPar exemple123.45 sera écrit 12345 avec multiplicateur de 100+Avec le compteur entier, comme chaque addition est exacte, les 30 000 additions ne produisent pas d'erreur de calcul. La seule opération réalisée sur des nombres réels (donc avec une erreur de précision) est la division par 3L'erreur de calcul finale est donc l'erreur sur une seule opération réellece qui reste négligeable.
  
-Erreur d'arrondi lors des conversionsmais les calculs sont exacts+<note>Le code pour réaliser ce test est relativement simple. Cependant, vous imaginez bien qu'il n'est pas possible de faire cela en écrivant 30 000 fois la ligne de code C++ pour faire l'addition. Pour celaon va utiliser une boucle qui sera vue par la suite. L’écriture de ce code sera proposée comme exercice.</note>
  
 +===== Lecture complémentaire =====
  
-Astuce : généralisable (mais s'appelle pas nombre à virgule fixe)Si par exemple compter de 1 à 10 avec pas de 1/3, compter de 1 à 30 et diviser pas 3+Voir l'article sur Wikipedia : [[https://en.wikipedia.org/wiki/Fixed-point_arithmetic|Fixed-point arithmetic]].
  
-^ Chapitre précédent ^ [[programmez_avec_le_langage_c|Sommaire principal]] ^ Chapitre suivant ^+[[complex|Chapitre précédent]] ^ [[programmez_avec_le_langage_c|Sommaire principal]] ^ [[ratio|Chapitre suivant]] ^
  
 {{tag> Cours C++}} {{tag> Cours C++}}
virgule_fixe.1435673358.txt.gz · Dernière modification: 2015/06/30 16:09 par gbdivers