Outils d'utilisateurs

Outils du Site


logique_et_calcul_booleen

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

logique_et_calcul_booleen [2014/05/28 18:44]
gbdivers
logique_et_calcul_booleen [2016/07/25 18:04] (Version actuelle)
gbdivers
Ligne 4: Ligne 4:
 ====== Logique binaire et calcul booléen ====== ====== Logique binaire et calcul booléen ======
  
-Comme vous l'avez vu au chapitre précédent, vous pouvez écrire les nombres en base 2, encore appelé binaireen utilisant la syntaxe ''0b''. Les nombres binaires ont un intérêt particulier dans le monde de l'informatique. Les ordinateurs fonctionnent à la base avec des signaux électriques, il est très facile de représenter une valeur pouvant prendre deux états en électronique. Par exemple, on peut dire qu'un tension basse (autour de 0 voltsreprésente un état et une tension haute (autour de 5 voltsà un autre état.+La représentation en base 2, le binaire, possède une importance particulière en informatique. Vous avez vu dans les chapitres précédents que vous pouvez représenter les nombres entiers sous forme binaire (une suite de 0 et de 1) en utilisant le préfixe ''0b''. Vous pouvez également écrire des valeurs booléennes en utilisant les mots-clés ''true'' (vrai) et ''false'' (faux).
  
-<note info>Cette représentation électronique des valeurs binaires en +0V et +5V était vrai dans les premiers ordinateurs, mais avec l'évolution des besoins et technologies, la situation actuelle est un peu plus compliquée. Le plus important à retire dans ce cours C++ est qu'une valeur binaire peut prendre deux états.</note>+Cette forme de logique est tellement importante qu'un chapitre complet lui est consacré (et vous reviendrez plusieurs fois sur ces notions durant le cours).
  
-Il est possible de nommer ces deux états de différentes manières, selon le contexte ou les habitudesOn peut par exemple parler d'états "haut" et "bas", "oui" et "non", "vrai" ou "faux", "0" ou "1", etc. En C++vous avez deux manière de représenter une valeur binaire :+En électronique, il est facile de représenter des valeurs binaires en utilisant des tensions différentes. Par exemple, on va définir que l'état "vraisera représenté par une tension de +5V et l'état "faux" par une tension de 0V(En pratiqueles valeurs prises seront très variables selon les composants de l'ordinateur, mais le principe reste le même.)
  
-  * pour écrire un nombre entier en utilisant le mode binaire avec ''0b'' et des 0 et 1 ; +Ces valeurs binaires utilisées en interne sont appelées "bits" et correspondent au plus petit élément d'information que peut manipuler un ordinateur. Tous les autres types de données (aussi bien les nombres que les chaînes de caractères) sont définis à partir d'une représentation interne en bits. Même les nombres binaires et les booléens que vous utilisez en C++ sont représentés par des bits dans l'ordinateur.
-  * pour écrire une valeur binaire, en utilisant les mots-clés ''true'' (vrai) et ''false'' (faux).+
  
 +Il est possible en C++ de manipuler directement les représentations internes des valeurs, mais cela est beaucoup plus complexe et moins sécurisé que de manipuler les types du C++. Vous n'aurez besoin de faire cela que dans des cas très spécifiques (programmation de micro-contrôleur, optimisation bas niveau), mais vous verrez comment faire cela.
 +
 +La notation des valeurs binaires "vrai" et "faux" est purement arbitraire. Vous pouvez définir que les valeurs binaires sont "haut" et "bas", "droite" et "gauche" ou n'importe quoi d'autre. Le plus important est que cela représente deux états différents. Dans du code C++, vous avez deux manières de représenter les valeurs binaires :
 +
 +  * avec 0 et 1, pour représenter un nombre entier ;
 +  * avec ''false'' et ''true'', pour représenter un booléen.
 ===== Les booléens ===== ===== Les booléens =====
  
 Voyons dans un premier temps les valeurs binaires, encore appelées booléens (nom donné en l'honneur du mathématicien [[http://fr.wikipedia.org/wiki/Alg%C3%A8bre_de_Boole_(logique)|George Boole]], qui a créé cette branche des mathématiques). Voyons dans un premier temps les valeurs binaires, encore appelées booléens (nom donné en l'honneur du mathématicien [[http://fr.wikipedia.org/wiki/Alg%C3%A8bre_de_Boole_(logique)|George Boole]], qui a créé cette branche des mathématiques).
  
-Pour écrire une valeur booléenne, il faut utiliser les mots-clé ''true'' (vrai) et ''false'' (faux). Par défaut, ''cout'' affiche ces valeurs avec respectivement 1 et 0. La directive ''std::boolalpha'' permet d'afficher les booléens en clair.+==== Afficher une valeur booléenne ==== 
 + 
 +Pour écrire une valeur booléenne, il faut utiliser les mots-clé ''true'' (vrai) et ''false'' (faux). Par défaut, ''std::cout'' affiche ces valeurs avec respectivement 1 et 0. La directive ''std::boolalpha'' permet d'afficher les booléens en clair et la directive ''std::noboolalpha'' permet d'arrêter de représenter les booléens.
  
 <code cpp main.cpp> <code cpp main.cpp>
Ligne 23: Ligne 30:
  
 int main() { int main() {
 +    std::cout << std::boolalpha;
     std::cout << "false = " << false << std::endl;     std::cout << "false = " << false << std::endl;
     std::cout << "true = " << true << std::endl;     std::cout << "true = " << true << std::endl;
-    std::cout << std::boolalpha;+    std::cout << std::noboolalpha;
     std::cout << "false = " << false << std::endl;     std::cout << "false = " << false << std::endl;
     std::cout << "true = " << true << std::endl;     std::cout << "true = " << true << std::endl;
Ligne 34: Ligne 42:
  
 <code> <code>
-false = 0 
-true = 1 
 false = false false = false
 true = true true = true
 +false = 0
 +true = 1
 </code> </code>
  
 ==== Les opérateurs logiques ==== ==== Les opérateurs logiques ====
  
-Les booléens ne se manipulent pas comme des nombres entiers et cela n'a pas de sens de faire des opérations arithmétiques dessus. Les booléens permettent un nombre limité d'opérations, qui prennent un ou deux booléens et retourne un nouveau booléen.+Les booléens ne se manipulent pas comme des nombres entiers. En effet, cela n'a pas de sens de faire des opérations arithmétiques dessus. Les booléens permettent un nombre limité d'opérations logiques, qui prennent un ou deux booléens et retournent un nouveau booléen
 + 
 +<note info>Faire des opérations arithmétiques sur les booléens ne provoquera pas d'erreur de compilation, vous pouvez donc écrire par exemple ''true + 2''. La raison est que les valeurs booléennes sont représentées en interne par des nombres (généralement 0 et 1) et que cela a un sens, **pour l'ordinateur**, de faire ce type d'opération.
  
-<note info>En fait, faire des opérations arithmétiques sur les booléens ne provoquera pas d'erreur de compilation, vous pouvez écrire par exemple ''true+2''. Il faut comprendre que le C++ est un langage permissif, qui fait confiance aux développeurs par défaut.+Mais cela n'a pas de sens en termes de logique (quel sens pourrait-on donner à l'expression ''true + 2'' ?).
  
-C'est donc à vous d'écrire du code en suivant certaines règles de codage et en respectant les sémantiquesEt doncmême si ''true+2'' est autorisé par la norme du langage, cela n'a pas de sens en termes de sémantique (si on vous demande "est-ce qu'il faut beau aujourd'hui ?", cela n'a pas de sens de réponse "oui plus deux").</note>+En C++, on utilisera les mots-clés ''true'' et ''false''. Le respect des types est l'une des forces du C++, encore faut-il les utiliser correctementLe C++ est un langage permissifil autorisera à écrire ''true + 2'', mais cela brisera la sémantique des booléens.</note>
  
 La première opération booléenne est la négation "NON" ''!'', qui transforme ''true'' en ''false'' et ''false'' en ''true'' : La première opération booléenne est la négation "NON" ''!'', qui transforme ''true'' en ''false'' et ''false'' en ''true'' :
Ligne 68: Ligne 78:
 </code> </code>
  
-La seconde opération est la conjonction ''&&'', qui prend deux booléens et retourne vrai uniquement si les deux booléens sont vrai. Cette opération est également appelée "AND" ou "ET", puisque pour que le résultat soit vrai, il faut que le premier booléen soit vrai ET que le second booléen soit vrai.+La seconde opération est la conjonction ''&&'', qui prend deux booléens et retourne vrai uniquement si les deux booléens valent vrai. Cette opération est également appelée "AND" ou "ET", puisque pour que le résultat soit vrai, il faut que le premier booléen soit vrai ET que le second booléen soit vrai.
  
 <code cpp main.cpp> <code cpp main.cpp>
Ligne 91: Ligne 101:
 </code> </code>
  
-La dernière opérateur est la disjonction ''||'', qui prend également deux booléens et retourne vrai si au moins un des deux booléens est vrai. Cette opération est également appelée "OR" ou "OU", puisque pour que le résultat soit vrai, il faut que le premier booléen soit vrai OU que le second booléen soit vrai.+La dernière opération est la disjonction ''||'', qui prend également deux booléens et retourne vrai si au moins un des deux booléens est vrai. Cette opération est également appelée "OR" ou "OU", puisque pour que le résultat soit vrai, il faut que le premier booléen soit vrai OU que le second booléen soit vrai.
  
  
Ligne 115: Ligne 125:
 </code> </code>
  
-Ces opérateurs peuvent être résumé dans un tableau :+<note>Il existe en théorie deux versions de la disjonction. L'opérateur ''||'' retourne vrai si les deux opérandes sont vraies, on dit que c'est un "OU inclusif". Il faut donc comprendre le "OU" de la façon suivante : "le premier booléen est vrai OU le second booléen est vrai OU les deux sont vrais". 
 + 
 +La seconde version de la disjonction est le "OU exclusif" ou "XOR". Dans ce cas, il faut prendre le "OU" au sens strict : "le premier booléen est vrai OU le second booléen est vrai, mais pas les deux en même temps". 
 + 
 +Il n'existe pas en C++ d'opérateur logique "Ou exclusif", mais il est possible de le simuler avec les autres opérateurs.</note> 
 + 
 +Ces opérateurs peuvent être résumés dans un tableau (appelé table de vérité) :
  
 ^  ''a''  ^  ''b''  ^  ''!a''  ^   ''a && b''  ^  ''a || b''  ^ ^  ''a''  ^  ''b''  ^  ''!a''  ^   ''a && b''  ^  ''a || b''  ^
Ligne 123: Ligne 139:
 |  1      |  1      |  0       |  1            |  1           | |  1      |  1      |  0       |  1            |  1           |
  
-Un point important pour terminer. Les opérateurs logiques du C++ fonctionne en utilisant l'évaluation paresseuse (//lazy evaluation//). Cela permet d'évaluer les opérandes uniquement si nécessaire. Imaginons les tests suivant :+Pour terminer, les opérateurs logiques du C++ fonctionnent en utilisant l'évaluation paresseuse (//lazy evaluation//). Cela permet d'évaluer les opérandes uniquement si nécessaire. Imaginons les opérations suivantes, dans lesquelles "expression complexe" est un code quelconque qui prend du temps pour être évalué :
  
 <code> <code>
-a && (expression compliquée+a && (expression complexe
-|| (expression compliquée)+|| (expression complexe)
 </code> </code>
  
-dans lesquelles "opération compliquée" est un code quelconque qui prend du temps a être évaluer. Avec le tableau précédent, on peut remarquer un point : si "aest faux, alors que le résultat de "a && b" sera toujours faux, quelque soit la valeur de "b". Dans ce cas, il n'est pas nécessaire d'évaluer "b", puisque sa valeur ne change par le résultat. Donc dans l'expression suivante :+Avec le tableau précédent, on peut remarquer que si ''a'' est faux, alors le résultat de ''a && (expression complexe)'' sera toujours faux, quelle que soit la valeur de ''expression complexe''. Dans ce cas, il n'est pas nécessaire d'évaluer "expression complexe", puisque sa valeur ne change pas le résultat.
  
-<code> +De la même façon, si ''b'' est vrai dans la seconde expression, le résultat de ''b || (expression complexe)'' sera toujours vrai quelle que soit la valeur de ''expression complexe'', il n'est pas nécessaire d'évaluer ''expression complexe''.
-a && (expression compliquée) +
-</code> +
- +
-Si "a" est faux, "expression compliquée" n'est pas évaluée et le résultat retournée sera faux. "Expression compliquée" ne sera évaluer que si "a" est vrai. +
- +
-De la même façon, si "a" est vrai dans la seconde expression avec "OR", le résultat sera toujours vrai et il n'est pas nécessaire d'évaluer "expression complexe""Expression compliquée" ne sera évaluer que si "a" est faux. +
- +
-<code> +
-a || (expression compliquée) +
-</code>+
  
 <note erreur>Cette technique permet de gagner en performances, en évitant de faire des calculs inutiles, mais cela implique une contrainte : il ne faut JAMAIS mettre dans une expression logique des calculs qui peuvent modifier le comportement du programme. Il sera plus sûr de séparer ces calculs et les opérations logiques dans le code.</note> <note erreur>Cette technique permet de gagner en performances, en évitant de faire des calculs inutiles, mais cela implique une contrainte : il ne faut JAMAIS mettre dans une expression logique des calculs qui peuvent modifier le comportement du programme. Il sera plus sûr de séparer ces calculs et les opérations logiques dans le code.</note>
Ligne 148: Ligne 154:
 ==== Les opérateurs de comparaison ==== ==== Les opérateurs de comparaison ====
  
-Généralement, vous n'aurez pas à écrire ''true'' et ''false'' directement dans vos codes, vous manipulerez des booléens générés par des tests. Un test est simplement une expression (une suite d'instructions et de calculs) qui retourne un booléen. Les opérateurs logiques permettent donc de combiner des tests simples pour former des tests plus complexes, voir très complexes.+Généralement, vous n'aurez pas à écrire ''true'' et ''false'' directement dans vos codes, vous manipulerez des booléens générés par des tests. Un test est simplement une expression (une suite d'instructions et de calculs) qui retourne un booléen. Les opérateurs logiques permettent de combiner des tests simples pour former des tests plus complexes, voire très complexes.
  
-La méthode classique pour écrire une expression retournant un booléen est d'utiliser les opérateurs de comparaison. Comme leur nom l'indique, ces opérateurs permettent de comparer des valeurs et de retourner vrai ou faux, selon le résultat de cette comparaison. Ces opérateurs sont les suivants :+Une méthode classique pour écrire une expression retournant un booléen est d'utiliser les opérateurs de comparaison. Comme leur nom l'indique, ces opérateurs permettent de comparer des valeurs et de retourner vrai ou faux, selon le résultat de cette comparaison. Ces opérateurs sont les suivants :
  
-  * l'opérateur "EST ÉGALE" ''=='' permet de tester si deux valeurs sont égale +  * l'opérateur "EST ÉGAL À" ''=='' permet de tester si deux valeurs sont égales 
-  * l'opérateur "EST DIFFÉRENT" ''!='' permet de tester si deux valeurs sont égale +  * l'opérateur "EST DIFFÉRENT DE" ''!='' permet de tester si deux valeurs sont différentes 
-  * l'opérateur "EST SUPÉRIEUR À" ''>'' permet de tester si la première valeur est supérieur à la seconde ; +  * l'opérateur "EST SUPÉRIEUR À" ''>'' permet de tester si la première valeur est supérieure à la seconde ; 
-  * l'opérateur "EST SUPÉRIEUR OU ÉGALE À" ''>='' permet de tester si la première valeur est supérieur ou est égale à la seconde ;+  * l'opérateur "EST SUPÉRIEUR OU ÉGAL À" ''>='' permet de tester si la première valeur est supérieure ou est égale à la seconde ;
   * l'opérateur "EST INFÉRIEUR À" ''<'' permet de tester si la première valeur est inférieure à la seconde ;   * l'opérateur "EST INFÉRIEUR À" ''<'' permet de tester si la première valeur est inférieure à la seconde ;
-  * l'opérateur "EST INFÉRIEUR OU ÉGALE À" ''<='' permet de tester si la première valeur est inférieure ou est égale à la seconde.+  * l'opérateur "EST INFÉRIEUR OU ÉGAL À" ''<='' permet de tester si la première valeur est inférieure ou est égale à la seconde.
  
-Ces opérateurs peuvent s'appliquer sur des nombres entiers ou réels ou les caractères, par exemple :+Ces opérateurs peuvent s'appliquer sur des nombres entiers, des réels ou des caractères, par exemple :
  
 <code cpp main.cpp> <code cpp main.cpp>
Ligne 188: Ligne 194:
 Pour les nombres, ces opérateurs ne posent pas de difficultés particulières, leur fonctionnement correspond à ce que vous connaissez en mathématique. Pour les nombres, ces opérateurs ne posent pas de difficultés particulières, leur fonctionnement correspond à ce que vous connaissez en mathématique.
  
-<note erreur>Les comparaison sur les chaînes de caractères est possible, mais nécessite quelques précautions. Vous verrez cela en détail dans le chapitre sur les chaînes.</note>+<note erreur>Les comparaisons sur les chaînes de caractères sont possibles, mais nécessitent quelques précautions. Vous verrez cela en détail dans __le chapitre sur les chaînes (lequel ?)__.</note>
  
-Comme le langage C++ est permissif, la comparaison de valeurs de type différent ne produira pas forcement une erreur de compilation. Par exemple comparer un entier et un caractère :+Comme le langage C++ est permissif, la comparaison de valeurs de types différents ne produira pas forcément une erreur de compilation. Par exemple comparer un entier et un caractère :
  
 <code cpp> <code cpp>
-std::cout << ('a'  < 42) << std::endl;+#include <iostream> 
 + 
 +int main() { 
 +    std::cout << std::boolalpha << ('a'  < 42) << std::endl; 
 +}
 </code> </code>
  
-Par contre, cela n'a pas de sens en termes de sémantique (comme on dit, on ne compte pas ensemble des pommes et des poires), il ne faut donc pas écrire ce type de code. En revanche, vous pouvez combiner le résultat de plusieurs comparaisons sur des valeurs de type différent en utilisant les opérateurs logiques :+Par contre, cela n'a pas de sens en termes de sémantique (comme on dit, on ne compte pas ensembles des pommes et des poires), il ne faut donc pas écrire ce type de code. En revanche, vous pouvez combiner le résultat de plusieurs comparaisons sur des valeurs de types différents en utilisant les opérateurs logiques :
  
 <code cpp> <code cpp>
-std::cout << (('a'  < 'z') && (123 >= 456)) << std::endl;+#include <iostream> 
 + 
 +int main() { 
 +    std::cout << std::boolalpha << (('a'  < 'z') && (123 >= 456)) << std::endl; 
 +}
 </code> </code>
  
-==== L'algèbre de bool ====+==== Exercices ====
  
-[[http://fr.wikipedia.org/wiki/Alg%C3%A8bre_de_Boole_(logique)|Algèbre de Boole]]+  * Evaluer le résultat de ces expressions (à la main, pas avec du code:
  
-===== La représentation binaire =====+^  ''a''  ^  ''b''  ^  ''!a && b''  ^   ''!a || b''  ^  ''!a && !b''  ^  ''!a || !b''  ^ 
 +|  0      |  0      |  ?            |  ?             |  ?             |  ?             | 
 +|  0      |  1      |  ?            |  ?             |  ?             |  ?             | 
 +|  1      |  0      |  ?            |  ?             |  ?             |  ?             | 
 +|  1      |  1      |  ?            |  ?             |  ?             |  ?             | 
 + 
 +  * Un OU Exclusif correspond à la table de vérité suivante. 
 + 
 +^  ''a''  ^  ''b''  ^  ''XOR''  ^ 
 +|  0      |  0      |  0        | 
 +|  0      |  1      |  1        | 
 +|  1      |  0      |  1        | 
 +|  1      |  1      |  0        | 
 + 
 +Dans le code suivant, replacer ''@@@@'' par une expression n'utilisant que les valeurs ''a'' et ''b'' et les opérateurs logiques ''&&'', ''||'' et ''!'', de façon à reproduire la table de vérité précédente.
  
-Voyons maintenant la représentation binaire des nombres entiers. Vous avez vu dans le chapitre précédent comment écrire un nombre selon cette représentation. Dans la représentation binaire, chaque chiffre 0 ou 1 est appelé un bitLorsque vous affichez ce nombre, la valeur est affichée (par défautselon la base 10 (décimale).+ 
 +<code cpp main.cpp> 
 +#include <iostream> 
 +  
 +bool eval(bool a,bool b) { 
 +    return (@@@@); 
 +
 +  
 +int main() { 
 +    std::cout << std::noboolalpha; 
 +    std::cout << "|  a  |  b  |  XOR   " << std::endl; 
 +    std::cout << "|  0  |  0  |   " << eval(false, false) << std::endl; 
 +    std::cout << "|  0  |  1  |   " << eval(false, true)  << std::endl; 
 +    std::cout << "|  1  |  0  |   " << eval(true,  false) << std::endl; 
 +    std::cout << "|  1  |  1  |   " << eval(true,  true)  << std::endl; 
 +
 +</code> 
 + 
 + 
 +===== Représentation binaire des entiers ===== 
 + 
 +Comme vous l'avez vu dans les chapitres précédents, la façon dont vous écrivez un nombre dans le code et la façon dont il est affiché dans la console sont indépendantsPar défautl'écriture d'un nombre et son affichage se font en utilisant la base 10 (décimal), mais vous pouvez changer la base lors de l'écriture en utilisant un préfixe (''0b'', ''0'' et ''0x'') et lors de l'affichage en utilisant une directive (''std::oct'', ''std::dec'' et ''std::hex'').
  
 <code cpp main.cpp> <code cpp main.cpp>
Ligne 214: Ligne 263:
  
 int main() { int main() {
-    std::cout << 0b101010 << std::endl;+    std::cout << std::showbase << 0b101010 << std::endl; 
 +    std::cout << std::hex << 0b101010 << std::endl;
 } }
 </code> </code>
Ligne 221: Ligne 271:
  
 <code> <code>
 +42
 42 42
 </code> </code>
  
-Il n'existe pas de directive pour afficher les nombres directement en binaire, on utilise souvent à la place le représentation hexadécimale. La raison est qu'il est relativement facile de faire la conversion entre hexadécimale et le binaire. En effet, un chiffre hexadécimal correspond exactement à quatre chiffres binaires, il faut donc utiliser la conversion suivante :+Il n'existe pas de directive pour afficher les nombres directement en binaire, on utilise souvent à la place la représentation hexadécimale. La raison est qu'il est relativement facile de faire la conversion entre hexadécimal et le binaire. En effet, un chiffre hexadécimal correspond exactement à quatre chiffres binaires, il faut donc utiliser la conversion suivante :
  
 ^  hexadécimal  ^  binaire  ^  hexadécimal  ^  binaire  ^ ^  hexadécimal  ^  binaire  ^  hexadécimal  ^  binaire  ^
Ligne 236: Ligne 287:
 |  7            |  0111     |  f            |  1111     | |  7            |  0111     |  f            |  1111     |
  
-Ainsi, pour convertir la valeur ''0x42'' en binaire, vous devez prendre le premier chiffre (''4''), le convertir en binaire (''0100''), puis faire la même chose avec le second chiffre (''2'', ce qui donne ''0010''). La représentation binaire finale est donc ''0b01000010''.+Ainsi, pour convertir la valeur ''0x2a'' en binaire, vous devez prendre le premier chiffre (''2''), le convertir en binaire (''0010''), puis faire la même chose avec le second "chiffre(''a''), ce qui donne ''1010''). La représentation binaire finale est donc ''0b00101010''
 + 
 +<note>Il est quand même possible d'afficher la représentation binaire d'un nombre, en utilisant la classe ''std::bitset''. Cette classe sera étudiée en détail dans un chapitre Complément, mais pour le moment, vous pouvez utiliser la sytnaxe : 
 + 
 +<code cpp main.cpp> 
 +#include <iostream> 
 +#include <bitset> 
 + 
 +int main() { 
 +    std::cout << "0b" << std::bitset<8>(0b101010) << std::endl; 
 +    std::cout << "0b" << std::bitset<8>(42) << std::endl; 
 +
 +</code> 
 + 
 +affiche : 
 + 
 +<code> 
 +0b00101010 
 +0b00101010 
 +</code> 
 + 
 +Le chiffre 8 correspond au nombre de bits à utiliser, pensez à l'adapter si vous utilisez des nombres entiers plus grands. Et n'oubliez pas la directive d'inclusion ''bitset''.</note> 
 + 
 +Comme cela a été expliqué au début du chapitre, toutes les valeurs que manipule un ordinateur sont en fait codées en interne en binaire. Cet encodage en binaire dans la mémoire de l'ordinateur est appelée //représentation binaire//. Vous n'aurez généralement pas besoin de manipuler directement les valeurs sous forme binaire, mais cette représentation est suffisamment importante pour que cela soit détaillé ici. 
 + 
 +La conversion des nombres entiers décimaux positifs en binaire est relativement simple. Pour les autres types de données (valeurs entières négatives, nombres réels, chaînes de caractères, etc.), la conversion n'est pas aussi simple et naturelle. Il existe en fait différentes normes de conversion, qui expliquent comment convertir une valeur d'un type donné en sa représentation binaire. Et il existe souvent plusieurs normes pour un même type de données. 
 + 
 +Un exemple classique de normalisation d'encodage concerne les caractères. Vous verrez qu'il existe des normes telles que "ASCII", "Windows-1252" ou "UTF-8". Au final, il existe des centaines de [[http://fr.wikipedia.org/wiki/Codage_des_caract%C3%A8res|formes différentes d'encodage]]. 
 + 
 +<note>Un point important à comprendre avec l'encodage : une même valeur pourra donner différentes représentations binaires, selon l'encodage utilisé. Et une même valeur binaire en mémoire pourra être représentée par différentes valeurs, de différents types, selon l'encodage utilisé. Ainsi, il est tout à fait possible de "convertir" un nombre réel en caractère en passant par une représentation binaire, mais cela n'aura généralement pas de sens. 
 + 
 +Heureusement, le compilateur vérifie les types que l'on utilise lorsque l'on fait des conversions, pour que cela conserve un sens. Il faudra juste faire attention de ne pas empêcher le compilateur de faire son travail. Mais nous reviendrons là dessus plus tard.</note> 
 + 
 +Une séquence de 8 bits (ou de 2 chiffres hexadécimaux, c'est équivalent) est appelée un octet (1 o), 1024 o donnent 1 kibi-octet (1 Kio), 1024 Kio donnent 1 mébi-octet (1 Mio), 1024 Mio donnent 1 gibi-octet (1 Gio) et 1024 Gio donnent 1 tebi-octet (1 Tio).
  
-<note info>La conversion entre nombres entier décimaux et leur représentation binaire est relativement simplemais retenez que toutes les informations contenu dans un ordinateur sont enregistrée au format binaireque ça soit du texte ou des nombres réelsLa conversion n'est alors pas aussi directe qu'avec les nombres entiers et il existe des normes pour faire ces conversions. +<note>Vous rencontrerez souvent une notation un peu différenteutilisant les préfixes du système métrique : kilo-, méga-giga- et téra-Généralement, ces suffixes seront équivalents, c'est-à-dire seront basé sur un rapport de 1 à 1024 entre deux unités de grandeur.
-</note>+
  
-Une séquence de 8 bits (ou de 2 chiffres hexadécimaux, cela revient au même) est appelée un octet (1 o), 1024 octets donnent 1 kilo octet (1 ko), 1024 ko donnent 1 méga octets (1 Mo), 1024 méga octets donnent 1 giga octets (1 Go), 1024 giga octets donnent 1 tera octets (1 To).+Cependant, avec le système métrique, le rapport devrait être de 1000 au lieu de 1024 et certains utilisent volontairement cette différence pour maintenir une ambiguïté chez le lecteur. Voir [[http://fr.wikipedia.org/wiki/Pr%C3%A9fixe_binaire|Préfixe binaire]] pour plus de détail.</note>
  
 ==== Les opérateurs arithmétiques ==== ==== Les opérateurs arithmétiques ====
  
-Maintenant que vous savez écrire et lire les nombres binaires, vous allez pouvoir les manipuler. Comme ce sont des nombres entiers, les opérateurs arithmétiques présentés dans le chapitre précédent peuvent être utilisé :+Maintenant que vous savez écrire et lire les nombres binaires, vous allez pouvoir les manipuler. Comme ce sont des nombres entiers, les opérateurs arithmétiques présentés dans le chapitre précédent peuvent être utilisés :
  
 <code cpp main.cpp> <code cpp main.cpp>
Ligne 265: Ligne 348:
 88 88
 4 4
 +</code>
 +
 +Notez bien que ces opérations donnent le même résultat que si vous aviez écrit les nombres en représentation binaire. Par exemple, pour la division :
 +
 +<code cpp main.cpp>
 +#include <iostream>
 +
 +int main() {
 +    std::cout << 0b1001 << std::endl;
 +    std::cout << 0b0010 << std::endl;
 +    std::cout << 9 / 2  << std::endl;
 +    std::cout << 0b1001 / 0b0010 << std::endl;
 +}
 +</code>
 +
 +affiche :
 +
 +<code>
 +9
 +2
 +4
 +4
 +</code>
 +
 +**Exos** : faire une addition et une multiplication binaire "à la main".
 +
 +<code cpp main.cpp>
 +#include <iostream>
 +#include <bitset>
 +
 +int main() {
 +    std::cout << "  " << std::bitset<8>(0b0100100) << std::endl;
 +    std::cout << "+ " << std::bitset<8>(0b0101001) << std::endl;
 +    std::cout << "  --------" << std::endl;
 +    std::cout << "= " << std::bitset<8>(0b0100100 + 0b0101001) << std::endl;
 +}
 +</code>
 +
 +affiche :
 +
 +<code>
 +  00100100
 ++ 00101001
 +  --------
 += 01001101
 </code> </code>
  
 ==== L'opérateur négation ==== ==== L'opérateur négation ====
  
-En complément de ces opérateurs arithmétiques, il existe des opérateurs travaillant sur la représentation binaire, que l'on appelle opérateurs logiques bit à bit. Le premier opérateur est la négation ''~'', qui permet d'inverser tous les bits d'un nombre (les 0 deviennent de 1 et les 1 deviennent des 0). Par exemple :+En complément de ces opérateurs arithmétiques, il existe des opérateurs travaillant sur la représentation binaire, que l'on appelle opérateurs logiques bit à bit. Le premier opérateur est la négation ''~'', qui permet d'inverser tous les bits d'un nombre (les 0 deviennent des 1 et les 1 deviennent des 0). Par exemple :
  
 <code cpp main.cpp> <code cpp main.cpp>
 #include <iostream> #include <iostream>
 +#include <bitset>
  
 int main() { int main() {
-    std::cout << std::hex << std::showbase;+    std::cout << " ~" << std::bitset<8>( 0b0100100) << std::endl; 
 +    std::cout << "= " << std::bitset<8>(~0b0100100) << std::endl;   
 +    std::cout << std::endl;  
 +    std::cout << " ~" << std::bitset<8>( 0b1001011) << std::endl; 
 +    std::cout << "= " << std::bitset<8>(~0b1001011) << std::endl; 
 +
 +</code> 
 + 
 +affiche : 
 + 
 +<code> 
 + ~00100100 
 += 11011011 
 + 
 + ~01001011 
 += 10110100 
 +</code> 
 + 
 +Si vous n'utilisez pas ''std::bitset'', mais affichez en héxadécimal, le résultat est un peu différent : 
 + 
 +<code cpp main.cpp> 
 +#include <iostream> 
 + 
 +int main() { 
 +    std::cout << std::showbase << std::hex; 
 +    std::cout <<  0b1 << std::endl;
     std::cout << ~0b1 << std::endl;     std::cout << ~0b1 << std::endl;
-    std::cout << ~0b110011 << std::endl;+    std::cout << std::endl; 
 +    std::cout <<  0b0110011 << std::endl; 
 +    std::cout << ~0b0110011 << std::endl;
 } }
 </code> </code>
Ligne 284: Ligne 440:
  
 <code> <code>
 +0x1
 0xfffffffe 0xfffffffe
 +
 +0x33
 0xffffffcc 0xffffffcc
 </code> </code>
Ligne 298: Ligne 457:
 </code> </code>
  
-Si on se rappelle que les 0 devant un nombre peuvent être ignorés (1 = 01 = 001 = 0001, etc.), on comprend que l'opération est réalisée sur des nombres entiers de 32 bits (ou 4 octets), quelque soit le nombre de bits que l'on utilise pour écrire le nombre. Les 0 manquant devant le nombre sont ajoutés avant l'opération.+__ ajouter un schéma, comme pour les opérateurs suivants __ 
 + 
 +Si on se rappelle que les 0 devant un nombre peuvent être ignorés (1 = 01 = 001 = 0001, etc.), on comprend que l'opération est réalisée sur des nombres entiers de 32 bits (ou 4 octets), quelque soit le nombre de bits que l'on utilise pour écrire le nombre. Les 0 manquants devant le nombre sont ajoutés avant l'opération. 
 + 
 +Cela signifie qu'en interne, ces nombres entiers sont représentés par défaut sur 32 bits (4 octets), //quelque soit le nombre de bits utilisés pour les écrire//. 
 + 
 +<note info>On pourrait penser que c'est du gâchis de mémoire d'utiliser 32 bits pour la représentation interne, alors que l'on écrit des nombres de 1 ou 6 bits. La raison est qu'un ordinateur est optimisé pour travailler avec des représentations de taille déterminée (généralement 32 ou 64 bits pour les ordinateurs de bureau). Le compilateur C++ adapte donc le nombre de bits en fonction de ce qui est le plus optimal pour l'ordinateur, mais il est possible de forcer l'utilisation de représentations de taille spécifique. Vous verrez cela dans un prochain chapitre.</note>
  
-<note info>On pourrait se dire que c'est un gâchis au niveau de la mémoire que l'ordinateur utilise systématiquement 32 bits, alors que l'on écrit des nombres sur 1 ou 6 bits. La raison est qu'un ordinateur est conçu pour optimiser le travail sur des nombres d'une certaine taille (32 ou 64 bits par exemple pour les ordinateurs de bureau), ce qui explique cette conversion. La taille optimale pour représenter un nombre dépend du type de processeur et du système d'exploitation, mais il est possible en C++ d'utiliser des nombres de taille différentes. Vous verrez cela dans un prochain chapitre.</note>+==== Le décalage de bits ====
  
 Un autre type d'opérateur logique sont les opérations de décalage à droite ''>>'' et à gauche ''<<''. Ces opération permettent de décaler les bits à droite ou à gauche d'un certain nombre de bits. Par exemple : Un autre type d'opérateur logique sont les opérations de décalage à droite ''>>'' et à gauche ''<<''. Ces opération permettent de décaler les bits à droite ou à gauche d'un certain nombre de bits. Par exemple :
Ligne 311: Ligne 476:
     std::cout << (0b11011000 << 1) << std::endl; // décalage de 1 bit à gauche     std::cout << (0b11011000 << 1) << std::endl; // décalage de 1 bit à gauche
     std::cout << (0b11011000 << 2) << std::endl; // décalage de 2 bit à gauche     std::cout << (0b11011000 << 2) << std::endl; // décalage de 2 bit à gauche
 +    std::cout << std::endl;
     std::cout << (0b11011000 >> 1) << std::endl; // décalage de 1 bit à droite     std::cout << (0b11011000 >> 1) << std::endl; // décalage de 1 bit à droite
     std::cout << (0b11011000 >> 2) << std::endl; // décalage de 2 bit à droite     std::cout << (0b11011000 >> 2) << std::endl; // décalage de 2 bit à droite
Ligne 316: Ligne 482:
 </code> </code>
  
-<note warning>Remarquez bien les parenthèses. Sans celle-ci, le compilateur ne pourra pas faire la différence entre les opérateur de décalage de bits ''<<'' et ''>>'' et les opérateurs de flux ''<<'' et ''>>'', ce qui ne produira pas le comportement attendu.+<note warning>Remarquez bien les parenthèses. Sans celle-ci, le compilateur ne pourra pas faire la différence entre les opérateurs de décalage de bits ''<<'' et ''>>'' et les opérateurs de flux ''<<'' et ''>>'', ce qui ne produira pas le comportement attendu.
  
-Plus généralement, il faudra faire attention en C++ à la syntaxe, un même opérateur pouvant signifiant des choses différentes selon le contexte.</note>+Plus généralement, il faudra faire attention en C++ à la syntaxe, un même opérateur pouvant signifier des choses différentes selon le contexte.</note>
  
 affiche : affiche :
Ligne 325: Ligne 491:
 0x1b0 0x1b0
 0x360 0x360
 +
 0x6c 0x6c
 0x36 0x36
Ligne 352: Ligne 519:
   * que le 0 à gauche est perdu.   * que le 0 à gauche est perdu.
  
-__ division et multiplication par 2, 4, etc avec les décalage __+**Exos** : comparer les division et multiplication par 2, 4, etc avec les décalage
  
 ==== Les opérateurs logiques bit à bit ==== ==== Les opérateurs logiques bit à bit ====
  
-Pour terminer, il existe les opérateurs logique "AND" ("ET") ''&'', "OR" ("OR") ''|'' et XOR ("OU Exclusif") ''^'' pour les nombres. Ils sont similaire aux opérateurs de même nom que vous avez vu précédemment pour les booléens, sauf qu'ils s'appliquent sur chaque bit d'un nombre. Ainsi, le premier bit du résultat est calculé à partir du premier bit de chaque nombre, le deuxième bit du résultat à partir du deuxième bit de chaque nombre, et ainsi de suite. L'opération "OU exclusif" n'a pas d'équivalent avec les booléens, il correspond à vrai lorsque une seule de valeur est vrai, pas les deux.+Pour terminer, il existe les opérateurs logiques "AND" ("ET") ''&'', "OR" ("OU") ''|'' et XOR ("OU Exclusif") ''^'' pour les nombres. Ils sont similaires aux opérateurs de même nom que vous avez vu précédemment pour les booléens, sauf qu'ils s'appliquent sur chaque bit d'un nombre. Ainsi, le premier bit du résultat est calculé à partir du premier bit de chaque nombre, le deuxième bit du résultat à partir du deuxième bit de chaque nombre, et ainsi de suite. L'opérateur "OU exclusif" n'a pas d'équivalent pour les booléens, pour rappel il retourne vrai lorsque l'une des opérandes est vraiemais pas les deux. 
 + 
 +Par exemple, pour l'opérateur "AND", on aura le schéma suivant :
  
 {{ :and.png |}} {{ :and.png |}}
Ligne 364: Ligne 533:
 <code cpp main.cpp> <code cpp main.cpp>
 #include <iostream> #include <iostream>
 +#include <bitset>
  
 int main() { int main() {
-    std::cout << std::hex << std::showbase+    std::cout << "  " << std::bitset<8>(0b1010) << std::endl
-    std::cout << (0b1010 & 0b1100) << std::endl; // AND +    std::cout <<; "  " << std::bitset<8>(0b1100) << std::endl; 
-    std::cout << (0b1010 | 0b1100) << std::endl; // OR +    std::cout << "  --------" << std::endl; 
-    std::cout << (0b1010 ^ 0b1100) << std::endl; // XOR+    std::cout << "& " << std::bitset<8>;(0b1010 & 0b1100) << std::endl; // AND 
 +    std::cout <<; "| " << std::bitset<8>;(0b1010 | 0b1100) << std::endl; // OR 
 +    std::cout <<; "^ " << std::bitset<8>;(0b1010 ^ 0b1100) << std::endl; // XOR
 } }
 </code> </code>
Ligne 376: Ligne 548:
  
 <code> <code>
-0x8 +  00001010 
-0xe +  00001100 
-0x6 +  -------- 
-&lt;/code> +&amp00001000 
- +| 00001110 
-Si on convertie en binaire, on obtient : +^ 00000110
- +
-<code> +
-0b1010 = 1 0 1 0 +
-0b1100 = 1 1 0 0 +
-0x8 =    1 0 0 0 // AND +
-0xe =    1 1 1 0 // OR +
-0x6 =    0 1 1 0 // XOR+
 </code> </code>
  
Ligne 399: Ligne 564:
 |  1      |  1      |  0       |  1           |  1          |  0          | |  1      |  1      |  0       |  1           |  1          |  0          |
  
-__ utilisation de mask avec opérateur logiques __ 
- 
-===== Exercices =====  
- 
-==== Algèbre de Bool ==== 
- 
-Il est possible d'écrire des opérations logiques complexes, à 2 ou plus arguments, avec les opérateurs de base NON, ET, OU et OU EXCLUSIF. 
- 
-  * Ecrire les séries suivantes : ??? 
- 
-  * En particulier, NON-ET permet de réécrire tous les autres opérateurs. Le faire. 
- 
-==== Les opérateurs arithmétiques ==== 
- 
-Dans un ordinateur, composé de transistors, forment des portes logiques. Ces portes permet de réaliser tous les calculs. 
- 
-  * pour 1 bit, réécrire l'addition avec les opération logiques 
-  * uniquement avec NAND 
-  * pour 8 bits, idem 
  
 ^ [[calculs_arithmetiques|Chapitre précédent]] ^ [[programmez_avec_le_langage_c|Sommaire principal]] ^ [[nombres_reels|Chapitre suivant]] ^ ^ [[calculs_arithmetiques|Chapitre précédent]] ^ [[programmez_avec_le_langage_c|Sommaire principal]] ^ [[nombres_reels|Chapitre suivant]] ^
  
-{{tag> Cours C++}} 
logique_et_calcul_booleen.1401295477.txt.gz · Dernière modification: 2014/05/28 18:44 par gbdivers