Auteur Sujet: Devinette  (Lu 6120 fois)

Pierre59

  • Sr. Member
  • ****
  • Messages: 321
    • Voir le profil
Devinette
« le: mai 31, 2020, 04:13:31 pm »

Bonjour

Il existe en C/C++ quatre opérateurs booléens :  !    &    |    ^   pour les opérations booléennes   non   et   ou   ou-exclusif   .

Par exemple  :    boolean a,b,c;    c=!a;   c=a&b;    c=a|b;    c=a^b;   

Trois de ces opérateurs peuvent êtres aussi utilisés sur des entiers, ils effectuent dans ce cas des   et   ou   ou-exclusif   bit à bit.

Par exemple :   int a,b,c;   c=a&b;    c=a|b;    c=a^b; 

Il existe aussi deux autres opérateurs booléens qui effectuent une optimisation   &&   ||   pour les opérations booléennes   et   ou    .

Pour ces deux opérateurs si on peux déterminer le résultat avec seulement le premier opérande, le deuxième n'est pas pris en compte. Pour le    et    si le premier opérande est faux le résultat est faux, pour le   ou   si le premier opérande est vrai le résultat est vrai.

Mais attention, les deux opérateurs ne sont pas équivalents dans certains cas, cela se produit quand le deuxième opérateur fait ce que l'on appelle "des effets de bord", cet effet de bord sera toujours fait avec l'opérateur normal, mais pas systématiquement avec l'opérateur optimisé.

Il existe aussi des opérateurs booléens combinés avec l'affectation  (comme pour beaucoup d'opérateurs) :       .

Par exemple :    boolean a,b;   a&=b;   a|=b;

Devinette, il existe des opérateurs optimisés pour le   et   et le   ou  , pourquoi il n'y en n'a pas pour le ou-exclusif ?

Pierre



Jean-Luc

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 1691
    • Voir le profil
Re : Devinette
« Réponse #1 le: juin 06, 2020, 05:15:29 pm »
Les opérateurs « optimisés » reposent sur le fait que FAUX est élément absorbant pour le ET et VRAI est élément absorbant pour le OU. Si le premier terme est élément absorbant pour l’opération, il n’est pas nécessaire d’évaluer le second.

Il n’y a pas d’opérateur « optimisé » pour le XOR car ni VRAI ni FAUX ne sont élément absorbant pour le XOR. Il est donc toujours nécessaire d’évaluer le second terme.

Ceci dit. && n’est pas équivalent a & (et || n’est pas équivalent à |) car le premier est un opérateur logique et le second un opérateur bit à bit, ils ne donnent pas le même résultat. Les opérateurs bit à bit, &, | et ^ ne sont pas des opérateurs logiques sauf à considérer que chaque bit des opérandes est un booléen. Ils ne rentrent donc pas dans la même catégorie que le !, le && et le ||.

PS : boolean est abnadonné, voir https://www.arduino.cc/reference/en/language/variables/data-types/boolean/
« Modifié: juin 07, 2020, 10:13:34 am par Jean-Luc »
Cordialement

Dominique

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 2889
  • 100% Arduino et N
    • Voir le profil
Re : Devinette
« Réponse #2 le: juin 07, 2020, 07:12:30 am »
Quel est l’intérêt concret de ces notions ?
Faut-il se méfier du compilateur ?
Cordialement,
Dominique

Jean-Luc

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 1691
    • Voir le profil
Re : Re : Devinette
« Réponse #3 le: juin 07, 2020, 10:10:37 am »
Quel est l’intérêt concret de ces notions ?

Comprendre la sémantique du langage permet de le maîtriser

Citer
Faut-il se méfier du compilateur ?

Oui et il faut également se méfier de soi même :)

En un mot comme en cent : il n'existe pas réellement de booléens en C ou en C++. Le compilateur ne dira rien si par exemple on écrit :

bool b = 40;

le compilateur est content. Il pourrait émettre un warning avec l'option -Wint-in-bool-context mais cette option n'est pas allumée dans l'IDE.

En appartée :

Je disais à Serrurier, sur un autre fil, que les documents sur le blog d'Eskimon n'étaient pas satisfaisants. Certes il a fini par supprimer ses tableaux qui grandissent tout seul mais il reste des choses comme :

Citer
Si on voulait faire un tableau de 100 étudiants dans lesquels on recense leurs nombres d’absence, on ferait le tableau suivant:

char absenteisme[100];

Je ne suis pas persuadé qu'un char soit le type adéquat pour compter quoique ça soit. La dissertation sur les booléens est également surprenante : https://eskimon.fr/tuto-arduino-105-le-langage-arduino-12#les-variables-booléennes

Le C est un langage très permissif. Avec le temps GCC a fini par ajouter des options permettant de corriger ce libéralisme débridé mais tout n'est pas activé dans l'IDE (probablement parce que ça empêcherait la compilation de bon nombre de bibliothèques et d'exemples).

Il faut donc un peu de discipline  :)
« Modifié: juin 07, 2020, 11:30:02 am par Jean-Luc »
Cordialement

Pierre59

  • Sr. Member
  • ****
  • Messages: 321
    • Voir le profil
Re : Devinette
« Réponse #4 le: juin 07, 2020, 11:24:55 am »

Bonjour

Quelques essais en Java (ou Processing) et en C++, concernant les opérateurs logiques.

En Java il y a un vrai type booléen ne pouvant être mélangé avec d'autres.

Soit une petite fonction f avec un effet de bord : boolean f(boolean b) { print("*"); return b; }
cette fonction affiche une étoile chaque fois que la fonction est appelée.

Le programme suivant teste les opérateurs logiques & && | || pour toutes les combinaisons de valeurs booléennes par le biais de la fonction f pour avoir des effets de bord :

  boolean a; boolean[] bs={false,true};
  println("et");
  for (boolean b : bs) for (boolean c : bs) {
    a=f(b)&f(c);  print(b,"&",c,"=",a," ");
    a=f(b)&&f(c); print(b,"&&",c,"=",a," "); println();
  }
  println("ou");
  for (boolean b : bs) for (boolean c : bs) {
    a=f(b)|f(c);  print(b,"|",c,"=",a," ");
    a=f(b)||f(c); print(b,"||",c,"=",a," "); println();
  }

cela donne :

et
**false & false = false  *false && false = false 
**false & true = false  *false && true = false 
**true & false = false  **true && false = false 
**true & true = true  **true && true = true 
ou
**false | false = false  **false || false = false 
**false | true = true  **false || true = true 
**true | false = true  *true || false = true 
**true | true = true  *true || true = true 

Les opérateurs & && | || sont bien des opérateurs logiques.

Passons au C++, la fonction avec effet de bord s'écrit : bool f(bool b) { cout<<"*"; return b; }

Le programme de test est semblable (à part les affichages) :

   bool a; bool bs[]={false,true};
   cout<<"et"<<endl;      
   for (bool i : bs) for (bool j : bs) {
      a=f(i)&f(j); cout<<i<<"&"<<j<<"="<<a<<" ";
      a=f(i)&&f(j); cout<<i<<"&&"<<j<<"="<<a<<" "; cout<<endl;
   }
   cout<<"ou"<<endl;      
   for (bool i : bs) for (bool j : bs) {
      a=f(i)|f(j); cout<<i<<"|"<<j<<"="<<a<<" ";
      a=f(i)||f(j); cout<<i<<"||"<<j<<"="<<a<<" "; cout<<endl;
   }

Le résultat est le même (à part l'affichage des booléens)

**0&0=0 *0&&0=0
**0&1=0 *0&&1=0
**1&0=0 **1&&0=0
**1&1=1 **1&&1=1
ou
**0|0=0 **0||0=0
**0|1=1 **0||1=1
**1|0=1 *1||0=1
**1|1=1 *1||1=1

En C++ les booléens sont en fait des entiers particuliers (on peut écrire 3+true ce qui donne 4).

En conclusion les opérateurs & && | || ressemblent bien à des opérateurs logiques. Mais en pratique dans les documentations Java ou C++ on n'en parle quasiment pas .

Cordialement

Pierre


Jean-Luc

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 1691
    • Voir le profil
Re : Devinette
« Réponse #5 le: juin 07, 2020, 11:32:10 am »
Pierre,

Si tu ne testes avec des valeurs 0 et 1, tu n’auras pas de différence entre & et && et | et ||. La différence vient quand les opérandes sont différents de 0 et 1.

Il est déconseillé, en C, d’utiliser & et | sur des booléens. Voir notamment le § 9.7 de ce document :

https://www.ssi.gouv.fr/uploads/2020/05/anssi-guide-regles_de_programmation_pour_le_developpement_securise_de_logiciels_en_langage_c-v1.1.pdf#page85

Et c’est également comme ça dans d’autres documents de bonnes pratiques comme MISRA-C
« Modifié: juin 07, 2020, 11:37:35 am par Jean-Luc »
Cordialement

Coyotte

  • Newbie
  • *
  • Messages: 17
    • Voir le profil
Re : Devinette
« Réponse #6 le: août 28, 2020, 11:24:17 am »
Bonjour,

Si je peux me permettre d'intervenir de manière constructive dans cet échange, je voudrais préciser quelques petites choses :

D'abord, et pour la suite des explications, je considère qu'un opérateur "optimisé" serait un opérateur qui fourni le même service mais de manière plus efficace.
A ce titre, && et || ne sont  pas des versions optimisées de & et |.

&, |, ^ et ! sont des opérateurs booléens qui manipulent des entiers et retournent un résultat sous la forme d'un entier.
&& et || sont des opérateurs logiques qui manipulent des concepts logiques (Vrai/Faux, True/False, ...) et retournent un résultat logique

13 & 11 = 9  tandis que 13 && 11 = True

De même,

12 | 3 = 15 tandis que 12 || 3 = True

Ce qui peut perturber la compréhension des différences entre && et & ou || et | est que la notion de type booléen n'existant pas à l'origine en C, on considère qu'un "Faux" logique est représenté par une valeur nulle et que toute valeur non nulle équivaut à un "Vrai" logique.

Imaginons maintenant que nous ayons deux variables a et b valant respectivement 12 (1100b) et 3 (0011b).

Si j'écris  If ( a && b )  cela revient à écrire en pseudo code  If ( ( 12 <> 0) && (3 <> 0 ) ) et le résultat est "Vrai".

Par contre, si j'écris  if (a & b ) le compilateur doit effectuer l'opération booléenne qui donne comme résultat 0, l'expression devient donc if( 0 ) et le résultat est "Faux".

On voit bien à travers cet exemple que && n'est pas une version optimisée de &.
J'espère que cette petite contribution permettra de mieux comprendre les implications de l'usage de l'un ou l'autre de ces opérateurs.
Bien à vous,

Michel




msport

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 2218
  • HO avec DCC++ en DIY Réseaux très éphémères
    • Voir le profil
Re : Re : Devinette
« Réponse #7 le: août 28, 2020, 03:17:27 pm »
PS : boolean est abandonné, voir https://www.arduino.cc/reference/en/language/variables/data-types/boolean/

Oui mais :  It’s recommended to instead use the standard type bool, which is identical.
Cordialement