Pourquoi 0 est faux?


115

Cette question peut sembler stupide, mais pourquoi 0évalue-t-elle falseet toute autre valeur [entière] trueest la plupart des langages de programmation?

Comparaison de chaîne

Comme la question semble un peu trop simple, je vais en expliquer un peu plus: tout d’abord, cela peut sembler évident à tout programmeur, mais pourquoi n’y aurait-il pas un langage de programmation - il se peut qu’il en existe, mais pas n'importe lequel. J'ai utilisé - où 0évalue trueet toutes les autres valeurs [entier] à false? Cette remarque peut sembler aléatoire, mais j’ai quelques exemples où cela aurait pu être une bonne idée. Tout d'abord, prenons l'exemple de la comparaison tridirectionnelle de chaînes, je prendrai strcmpcomme exemple l'exemple de C : tout programmeur essayant d'utiliser C comme première langue peut être tenté d'écrire le code suivant:

if (strcmp(str1, str2)) { // Do something... }

Depuis strcmpreturn, 0qui évalue à falsequand les chaînes sont égales, ce que le programmeur a essayé de faire échoue lamentablement et il ne comprend généralement pas pourquoi au début. Si elle avait été 0évaluée à la trueplace, cette fonction aurait pu être utilisée dans sa plus simple expression - celle ci-dessus - pour comparer l'égalité et les vérifications appropriées pour -1et 1n'auraient été effectuées qu'en cas de besoin. Nous aurions considéré le type de retour comme bool(dans notre esprit je veux dire) la plupart du temps.

De plus, introduisons un nouveau type sign, qui prend juste des valeurs -1, 0et 1. Cela peut être très pratique. Imaginez qu'il y ait un opérateur de vaisseau spatial en C ++ et que nous le voulions std::stringbien (bon, il y a déjà une comparefonction, mais l'opérateur de vaisseau spatial est plus amusant). La déclaration serait actuellement la suivante:

sign operator<=>(const std::string& lhs, const std::string& rhs);

S'il avait 0été évalué true, l'opérateur du vaisseau spatial n'existerait même pas, et nous aurions pu le déclarer operator==ainsi:

sign operator==(const std::string& lhs, const std::string& rhs);

Cela operator==aurait traité la comparaison à trois voies à la fois, et pourrait toujours être utilisé pour effectuer le contrôle suivant tout en pouvant vérifier quelle chaîne est lexicographiquement supérieure à l'autre le cas échéant:

if (str1 == str2) { // Do something... }

Gestion des erreurs anciennes

Nous avons maintenant des exceptions, donc cette partie ne s’applique qu’aux anciennes langues où il n’en existait pas (C par exemple). Si nous regardons la bibliothèque standard de C (et celle de POSIX également), nous pouvons voir avec certitude que les fonctions maaaaany retournent en 0cas de succès et que tout nombre entier sinon. J'ai malheureusement vu des gens faire ce genre de choses:

#define TRUE 0
// ...
if (some_function() == TRUE)
{
    // Here, TRUE would mean success...
    // Do something
}

Si nous réfléchissons à notre façon de penser en programmation, nous avons souvent le schéma de raisonnement suivant:

Do something
Did it work?
Yes ->
    That's ok, one case to handle
No ->
    Why? Many cases to handle

Si nous y repenions, il aurait été logique de mettre la seule valeur neutre 0, à yes(et c'est ainsi que fonctionnent les fonctions de C), alors que toutes les autres valeurs peuvent être là pour résoudre les nombreux cas de la no. Cependant, dans tous les langages de programmation que je connais (sauf peut - être quelques langues expérimentales ésotériques), qui yesévalue à falsedans un ifétat, alors que tous les nocas à évaluer true. Il existe de nombreuses situations où "ça marche" représente un cas alors que "ça ne marche pas" représente plusieurs causes probables. Si nous y réfléchissons de la sorte, avoir 0évalué à trueet au reste falseaurait eu beaucoup plus de sens.

Conclusion

Ma conclusion est essentiellement ma question initiale: pourquoi avons-nous conçu les langages où 0sont falseet sont les autres valeurs true, en prenant en compte mes quelques exemples ci-dessus et peut-être d'autres que je n'ai pas pensé?

Suivi: Il est agréable de voir qu'il existe de nombreuses réponses avec de nombreuses idées et autant de raisons possibles pour que cela se produise. J'aime à quel point vous semblez passionné. Au départ, j’avais posé cette question par ennui, mais comme vous paraissiez si passionné, j’ai décidé d’aller un peu plus loin et de demander pourquoi le choix booléen pour 0 et 1 sur Math.SE :)


32
strcmp()n’est pas un bon exemple pour true ou false, car il renvoie 3 valeurs différentes. Et vous serez surpris lorsque vous commencerez à utiliser un shell, où 0 signifie vrai et tout le reste faux.
ott--

52
@ ott--: Dans les shells Unix, 0 signifie succès et non-zéro signifie échec - pas tout à fait la même chose que "vrai" et "faux".
Keith Thompson

14
@KeithThompson: Dans Bash (et d'autres coquilles), "succès" et "échec" sont vraiment identiques à "vrai" et "faux". Considérons, par exemple, l’instruction suivante if true ; then ... ; fi: où trueest une commande qui renvoie zéro et qui demande ifà être exécutée ....
Ruakh

13
Il n'y a pas de booléens dans le matériel, uniquement des nombres binaires, et dans la plupart des ISA historiques, un nombre non nul est considéré comme "vrai" dans toutes les instructions de branchement conditionnel (à moins qu'elles n'utilisent des indicateurs). Ainsi, les langages de bas niveau sont obligés de suivre les propriétés matérielles sous-jacentes.
SK-logic

2
@MasonWheeler Avoir un type booléen n'implique rien. Par exemple python n'avoir un type , mais les comparaisons / si les conditions , etc. peuvent avoir une valeur de retour. bool
Bakuriu

Réponses:


98

0est falseparce qu'ils sont tous les deux des éléments nuls dans les semi- communes . Même s'il s'agit de types de données distincts, il est logique de procéder à la conversion entre eux, car ils appartiennent à des structures algébriques isomorphes.

  • 0est l'identité pour l'addition et zéro pour la multiplication. Cela est vrai pour les entiers et les rationnels, mais pas pour les nombres à virgule flottante IEEE-754: 0.0 * NaN = NaNet 0.0 * Infinity = NaN.

  • falseest l'identité pour booléenne XOR (⊻) et zéro pour booléenne et (∧). Si les booléens sont représentés par {0, 1} - l'ensemble des entiers modulo 2 -, vous pouvez considérer comme addition sans retenue et comme multiplication.

  • ""et []sont une identité pour la concaténation, mais il y a plusieurs opérations pour lesquelles elles ont un sens comme zéro. La répétition en est une, mais la répétition et la concaténation ne sont pas distribuées et ces opérations ne forment donc pas un semiring.

De telles conversions implicites sont utiles dans les petits programmes, mais dans l’ensemble, elles peuvent rendre les programmes plus difficiles à raisonner. Un des nombreux inconvénients de la conception linguistique.


1
Bien que vous ayez mentionné des listes. (BTW, nilà la fois la liste vide []et la falsevaleur dans Common Lisp; existe-t-il une tendance à fusionner les identités provenant de différents types de données?) Vous devez toujours expliquer pourquoi il est naturel de considérer faux comme une identité additive et vrai comme une identité multiplicative. et non l'inverse. N'est-il pas possible de considérer truecomme l'identifiant ANDet le zéro OR?
Giorgio

3
+1 pour faire référence à des identités similaires. Enfin, une réponse qui ne se résume pas à "une convention, traite-la".
l0b0

5
+1 pour avoir donné les détails d'une mathématique concrète et très ancienne dans laquelle cela a été suivi et a longtemps eu un sens
Jimmy Hoffa

1
Cette réponse n'a pas de sens. trueest aussi l'identité et le zéro des semirings (booléens et / ou). Il n'y a aucune raison, à part la convention, à considérer falseest plus proche de 0 que true.
TonioElGringo

1
@TonioElGringo: la différence entre vrai et faux est la différence entre XOR et XNOR. On peut former des anneaux isomorphes en utilisant AND / XOR, où true est l’identité multiplicative et false l’autre additive, ou avec OR et XNOR, où false est l’identité multiplicative et true est l’additive, mais XNOR n’est généralement pas considéré comme commun. opération fondamentale comme XOR.
Supercat

74

Parce que les maths fonctionnent.

FALSE OR TRUE is TRUE, because 0 | 1 is 1.

... insert many other examples here.

Traditionnellement, les programmes C ont des conditions comme

if (someFunctionReturningANumber())

plutôt que

if (someFunctionReturningANumber() != 0)

parce que le concept de zéro étant équivalent à faux est bien compris.


21
Les langues sont conçues comme ça parce que les maths ont du sens. C'est venu en premier.
Robert Harvey

26
@ Morwenn, cela remonte au 19ème siècle et à George Boole. Les gens représentent Faux comme 0 et Vrai comme! 0 depuis plus longtemps qu'il n'y a eu d'ordinateurs.
Charles E. Grant

11
Je ne vois pas pourquoi les calculs ne fonctionneraient pas dans l'autre sens si vous modifiez simplement toutes les définitions pour que AND soit + et OU soit *.
Neil G

7
Exactement: le calcul fonctionne dans les deux sens et la réponse à cette question semble être qu'elle est purement conventionnelle.
Neil G

6
@Robert Ce serait formidable si vous pouviez épeler les "fondements mathématiques" dans votre message.
Phant0m

38

Comme d'autres l'ont dit, le calcul est venu en premier. C'est pourquoi 0 est falseet 1 est true.

De quelles mathématiques parlons-nous? Algèbres booléennes datant du milieu des années 1800, bien avant l'arrivée des ordinateurs numériques.

Vous pouvez également dire que la convention est issue de la logique propositionnelle , qui est même plus ancienne que les algèbres booléennes. C'est la formalisation de beaucoup de résultats logiques que les programmeurs connaissent et aiment ( false || xégaux x, true && xégaux x, etc.).

Fondamentalement, nous parlons d'arithmétique sur un ensemble avec deux éléments. Pensez à compter en binaire. Les algèbres booléennes sont à l'origine de ce concept et de son fondement théorique. Les conventions de langages comme C ne sont qu’une application simple.


2
Vous pourriez, bien sûr. Mais le garder à la manière «standard» s’accorde bien avec l’arithmétique générale (0 + 1 = 1 et non 0 + 1 = 0).
joshin4colours

2
Oui, mais vous devez probablement écrire AND avec + et OU avec * si vous inversez également les définitions.
Neil G

3
Le calcul n'est pas venu en premier. Math a reconnu que 0 et 1 forment un champ dans lequel AND correspond à la multiplication et OR correspond à l'addition.
Kaz

1
@Kaz: Mais {0, 1} avec OR et AND ne forme pas un champ.
Giorgio

2
Cela me gène un peu que plus de réponses et commentaires disent que true = 1. Ce n'est pas tout à fait exact, car true != 0ce n'est pas exactement la même chose. Une des raisons (et non la seule) pour laquelle il faut éviter les comparaisons telles que if(something == true) { ... }.
JensG

26

Je pensais que cela avait à voir avec "l'héritage" de l'électronique, et aussi l'algèbre booléenne, où

  • 0= off, negative, no,false
  • 1= on, positive, yes,true

strcmp renvoie 0 lorsque les chaînes sont égales, cela a à voir avec sa mise en œuvre, car ce qu'il fait est de calculer la "distance" entre les deux chaînes. Que 0 soit également considéré comme faux est simplement une coïncidence.

Il est logique de renvoyer 0 en cas de succès, car 0 signifie dans ce cas aucune erreur et tout autre chiffre constituerait un code d'erreur. Utiliser n'importe quel autre numéro pour réussir aurait moins de sens puisque vous n'avez qu'un seul code de réussite, alors que vous pouvez avoir plusieurs codes d'erreur. Vous utilisez "Cela a-t-il fonctionné?" comme si l'expression de déclaration et dire 0 = oui aurait plus de sens, mais l'expression est plus correctement "Est-ce que quelque chose s'est mal passé?" et ensuite vous voyez que 0 = non a beaucoup de sens. Penser false/truen'a pas vraiment de sens ici, comme c'est en fait no error code/error code.


Haha, vous êtes le premier à énoncer explicitement la question d'erreur de retour. Je savais déjà que je l'avais interprété à ma manière et cela pouvait être demandé de manière différente, mais vous êtes le premier à l'exprimer explicitement (à partir des nombreuses réponses et commentaires). En fait, je ne dirais pas que l'une ou l'autre manière n'a aucun sens, mais plutôt que les deux ont un sens de différentes manières :)
Morwenn

1
En fait , je dirais que 0pour success/no errorest la seule chose qui est logique quand d' autres entiers représentent les codes d'erreur. Cela 0arrive aussi à représenter falsedans d' autres cas ne compte pas vraiment, puisque nous ne parlons pas de vrai ou faux tout ici;)
Svish

J'ai eu la même idée alors j'ai
levé

1
Votre remarque sur le strcmp()calcul de la distance est assez bonne. Si cela avait été appelé, strdiff()ce if (!strdiff())serait très logique.
Kevin Cox

"electronics [...] où 0 = [...] false, 1 = [...] true" - même en électronique, il ne s'agit que d'une convention , et elle n'est pas la seule. Nous appelons cela la logique positive, mais vous pouvez également utiliser la logique négative, où une tension positive indique faux et une négative indique vraie. Ensuite, le circuit que vous utiliseriez pour ET devient OU, OU devient ET, et ainsi de suite. En raison de la loi de De Morgan, tout cela finit par être équivalent. Parfois, vous trouverez une partie d'un circuit électronique implémenté dans une logique négative pour plus de commodité. Les noms des signaux de cette partie sont alors signalés par une barre au-dessus d'eux.
Jules

18

Comme expliqué dans cet article , les valeurs falseet truene doivent pas être confondues avec les entiers 0 et 1, mais peuvent être identifiées avec les éléments du corps de Galois (champ fini) de deux éléments (voir ici ).

Un champ est un ensemble avec deux opérations qui satisfont certains axiomes.

Les symboles 0 et 1 sont classiquement utilisés pour désigner les identités additives et multiplicatives d'un champ car les nombres réels sont aussi un champ (mais pas un nombre fini) dont les identités sont les nombres 0 et 1.

L'identité additive est l'élément 0 du champ, tel que pour tout x:

x + 0 = 0 + x = x

et l'identité multiplicative est l'élément 1 du champ, tel que pour tout x:

x * 1 = 1 * x = x

Le corps fini de deux éléments n'a que ces deux éléments, à savoir l'identité additive 0 (ou false) et l'identité multiplicative 1 (ou true). Les deux opérations de ce champ sont le XOR logique (+) et le ET logique (*).

Remarque. Si vous inversez les opérations (XOR est la multiplication et AND est l'addition), la multiplication n'est pas distributive sur l'addition et vous n'avez plus de champ. Dans un tel cas, vous n'avez aucune raison d'appeler les deux éléments 0 et 1 (dans n'importe quel ordre). Notez également que vous ne pouvez pas choisir l'opération OR à la place de XOR: quelle que soit la façon dont vous interprétez OR / AND comme addition / multiplication, la structure résultante n'est pas un champ (tous les éléments inverses n'existent pas comme l'exigent les axiomes du champ).

En ce qui concerne les fonctions C:

  • De nombreuses fonctions renvoient un entier représentant un code d'erreur. 0 signifie AUCUNE ERREUR.
  • Intuitivement, la fonction strcmpcalcule la différence entre deux chaînes. 0 signifie qu'il n'y a pas de différence entre deux chaînes, c'est-à-dire que deux chaînes sont égales.

Les explications intuitives ci-dessus peuvent aider à mémoriser l'interprétation des valeurs de retour, mais il est encore plus simple de consulter la documentation de la bibliothèque.


1
+1 pour montrer que si vous les échangez arbitrairement, les calculs ne fonctionnent plus.
Jimmy Hoffa,

2
Retourné: Étant donné un champ avec deux éléments et opérations * et +, nous identifions Vrai avec 0 et Faux avec 1. Nous identifions OR avec * et XOR avec +.
Neil G

1
Vous constaterez que ces deux identifications sont effectuées sur le même champ et qu'elles sont cohérentes avec les règles de la logique booléenne. Votre note est malheureusement incorrecte :)
Neil G

1
Si vous supposez que True = 0 et que XOR est +, alors True doit être l'identité de XOR. Mais ce n'est pas parce que True XOR True = False. Sauf si vous redéfinissez l'opération XOR sur True pour que True XOR True = True. Ensuite, bien sûr, votre construction fonctionne parce que vous venez de renommer des éléments (dans toute structure mathématique, vous pouvez toujours réussir à permuter un nom et obtenir une structure isomorphe). D'autre part, si vous laissez Vrai, Faux et XOR ont leur signification habituelle, alors Vrai XOR Vrai = Faux et Vrai ne peut pas être l'identité additive, c’est-à-dire que Vrai ne peut pas être 0.
Giorgio

1
@Giorgio: J'ai corrigé ma construction par votre commentaire dans mon dernier commentaire…
Neil G

13

Vous devriez considérer que les systèmes alternatifs peuvent également être des décisions de conception acceptables.

Coques: 0 l'état de sortie est vrai, non-zéro est faux

L'exemple de shells traitant un statut de sortie 0 comme étant true a déjà été mentionné.

$ ( exit 0 ) && echo "0 is true" || echo "0 is false"
0 is true
$ ( exit 1 ) && echo "1 is true" || echo "1 is false"
1 is false

La raison en est qu'il y a un moyen de réussir, mais de nombreuses façons d'échouer. Par conséquent, utiliser 0 comme valeur spéciale signifiant "pas d'erreur" est pragmatique.

Ruby: 0 est comme n'importe quel autre numéro

Parmi les langages de programmation "normaux", il existe des valeurs aberrantes, telles que Ruby, qui traitent 0 comme une valeur vraie.

$ irb
irb(main):001:0> 0 ? '0 is true' : '0 is false'
=> "0 is true"

Le raisonnement est que seulement falseet nildevrait être faux. Pour beaucoup de novices Ruby, c'est un piège. Cependant, dans certains cas, il est agréable que 0 soit traité comme n'importe quel autre nombre.

irb(main):002:0> (pos = 'axe' =~ /x/) ? "Found x at position #{pos}" : "x not found"
=> "Found x at position 1"
irb(main):003:0> (pos = 'xyz' =~ /x/) ? "Found x at position #{pos}" : "x not found"
=> "Found x at position 0"
irb(main):004:0> (pos = 'abc' =~ /x/) ? "Found x at position #{pos}" : "x not found"
=> "x not found"

Cependant, un tel système ne fonctionne que dans une langue capable de distinguer les booléens d'un type distinct des nombres. Au début de l'informatique, les programmeurs travaillant avec le langage assembleur ou le langage machine brut n'avaient pas ce luxe. Il est probablement naturel de considérer 0 comme l'état "vide" et de définir un bit sur 1 comme indicateur lorsque le code détecte que quelque chose s'est passé. Par extension, la convention élaborée établissait que zéro était traité comme faux et que les valeurs non nulles devenaient vraies. Cependant, il n'est pas nécessaire que ce soit comme ça.

Java: les nombres ne peuvent pas être traités comme des booléens

En Java, trueet falsesont les seules valeurs booléennes. Les nombres ne sont pas des booléens et ne peuvent même pas être convertis en booléens ( spécification du langage Java, section 4.2.2 ):

Il n'y a pas d'incantation entre les types intégraux et le type boolean.

Cette règle évite simplement la question - toutes les expressions booléennes doivent être explicitement écrites dans le code.


1
Rebol et Red traitent tous les deux un entier à valeur 0! valeurs comme vrai, et ont un NONE séparé! type (avec une seule valeur, NONE) traité comme conditionnel false en plus de LOGIC! faux. J'ai trouvé une frustration importante en essayant d'écrire du code JavaScript qui traite 0 comme faux. c'est une décision incroyablement maladroite pour un langage à typage dynamique. Si vous voulez tester quelque chose qui peut être nul ou nul, vous finissez par avoir à écrire if (thing === 0), ce n'est tout simplement pas cool.
HostileFork

@ HostileFork Je ne sais pas. Je trouve que cela a du sens que ce 0soit true(comme tous les autres entiers) dans un langage dynamique. Il m’arrivait parfois d’attraper un 0problème en essayant d’attraper Noneen Python, et cela peut parfois être assez difficile à repérer.
Morwenn

2
Ruby n'est pas une valeur aberrante. Ruby prend ceci de Lisp (Ruby est même secrètement appelée "MatzLisp"). Lisp est un langage courant en informatique. Le zéro est également juste une vraie valeur dans le shell POSIX, car il est un morceau de texte: if [ 0 ] ; then echo this executes ; fi. La valeur de données fausses est une chaîne vide, et un mensonge testable est un état de terminaison manqué d'une commande, qui est représenté par un non -Zero.
Kaz

8

Avant d'aborder le cas général, nous pouvons discuter de vos contre-exemples.

Comparaisons de chaînes

La même chose vaut pour beaucoup de sortes de comparaisons, en fait. De telles comparaisons calculent une distance entre deux objets. Lorsque les objets sont égaux, la distance est minimale. Ainsi, lorsque la "comparaison réussit", la valeur est 0. Mais en réalité, la valeur de retour de strcmpn'est pas un booléen, c'est une distance et c'est ce qui piège les programmeurs ignorants if (strcmp(...)) do_when_equal() else do_when_not_equal().

En C ++, nous pourrions reconcevoir strcmppour renvoyer un Distanceobjet, qui se substitue operator bool()à vrai lorsque 0 (mais vous seriez alors piqué par un ensemble de problèmes différent). Ou simplement, C a juste une streqfonction qui retourne 1 quand les chaînes sont égales, et 0 sinon.

Appels API / code de sortie du programme

Ici, vous vous souciez de la raison pour laquelle quelque chose a mal tourné, car cela poussera les décisions vers le haut en erreur. Lorsque les choses réussissent, vous ne voulez rien savoir de particulier, votre intention est réalisée. La valeur de retour doit donc transmettre cette information. Ce n'est pas un booléen, c'est un code d'erreur. La valeur d'erreur spéciale 0 signifie "pas d'erreur". Le reste de la plage représente les erreurs significatives localement que vous devez traiter (y compris 1, ce qui signifie souvent "erreur non spécifiée").

Cas général

Cela nous laisse avec la question: pourquoi les valeurs booléennes Trueet Falsecommunément représentées avec 1 et 0, respectivement?

Eh bien, outre l'argument subjectif "on se sent mieux de cette façon", voici quelques raisons (subjectives aussi) auxquelles je peux penser:

  • Analogie avec un circuit électrique. Le courant est activé pour 1s et désactivé pour 0s. J'aime avoir (1, oui, vrai, activé) ensemble et (0, non, faux, désactivé), plutôt qu'un autre mélange

  • initialisations de la mémoire. Quand je suis memset(0)un groupe de variables (que ce soit des ints, des floats, des bools), je veux que leur valeur corresponde aux hypothèses les plus conservatrices. Par exemple, ma somme est initialement égale à 0, le prédicat est faux, etc.

Peut-être que toutes ces raisons sont liées à mon éducation - si on m'avait appris à associer 0 à True dès le début, j'aurais choisi l'inverse.


2
En fait, il existe au moins un langage de programmation qui considère que 0 est vrai. Le shell unix.
Jan Hudec

+1 pour aborder le vrai problème: la plupart de la question de Morwenn n'est pas booldu tout.
dan04

@ dan04 C'est ça. Le poste entier est sur la raison d'être le choix de la distribution de intla booldans de nombreux langages de programmation. La comparaison et la gestion des erreurs ne sont que des exemples d’endroits où il serait logique de le formuler autrement.
Morwenn

6

D'un point de vue général, vous parlez de trois types de données très différents:

  1. Un booléen. La convention mathématique en algèbre booléenne est d'utiliser 0 pour falseet 1 pour true, il est donc logique de suivre cette convention. Je pense que cette façon de faire est également plus logique intuitivement.

  2. Le résultat de la comparaison. Cela a trois valeurs: <, =et >(notez qu'aucun d'entre eux est true). Pour eux, il est judicieux d'utiliser les valeurs -1, 0 et 1, respectivement (ou, plus généralement, une valeur négative, zéro et une valeur positive).

    Si vous voulez vérifier l’égalité et que vous n’avez qu’une fonction qui effectue une comparaison générale, je pense que vous devriez la rendre explicite en utilisant quelque chose comme strcmp(str1, str2) == 0. L'utilisation !de cette situation est source de confusion car elle traite une valeur non booléenne comme s'il s'agissait d'un booléen.

    Aussi, gardez à l'esprit que la comparaison et l'égalité ne doivent pas nécessairement être la même chose. Par exemple, si vous commandez des personnes en fonction de leur date de naissance, elles Compare(me, myTwin)devraient revenir 0, mais Equals(me, myTwin)devraient y revenir false.

  3. Le succès ou l'échec d'une fonction, éventuellement avec des détails sur ce succès ou cet échec. Si vous parlez de Windows, ce type est appelé HRESULTet une valeur non nulle n'indique pas nécessairement un échec. En fait, une valeur négative indique un échec et un succès non négatif. La valeur de réussite est très souvent S_OK = 0, mais il peut également s'agir par exemple S_FALSE = 1, ou d'autres valeurs.

La confusion vient du fait que trois types de données logiquement très différents sont représentés sous la forme d’un seul type de données (un entier) en C et dans d’autres langues et que vous pouvez utiliser un entier dans une condition. Mais je ne pense pas qu'il serait logique de redéfinir booléen pour rendre l'utilisation de certains types non booléens dans des conditions plus simples.

En outre, considérons un autre type souvent utilisé dans une condition de C: un pointeur. Là-bas, il est naturel de traiter un NULLpointeur (représenté par 0) comme false. Il serait donc également plus difficile de travailler avec des indicateurs en suivant votre suggestion. (Bien que, personnellement, je préfère explicitement comparer les pointeurs avec NULL, au lieu de les traiter comme des booléens.)


4

Zéro peut être faux car la plupart des processeurs ont un indicateur ZÉRO qui peut être utilisé pour créer une branche. Il enregistre une opération de comparaison.

Voyons pourquoi.

Certains psuedocode, car le public ne lit probablement pas assemblage

c-source appelle des appels de boucle simples wibble 10 fois

for (int foo =10; foo>0; foo-- ) /* down count loop is shorter */
{  
   wibble();
}

certains prétendent assembler pour cela

0x1000 ld a 0x0a      'foo=10
0x1002 call 0x1234    'call wibble()
0x1005 dec a          'foo--
0x1006 jrnz -0x06      'jump back to 0x1000 if not zero
0x1008  

c- source une autre simple boucle appelle wibble 10 fois

for (int foo =0; foo<10; foo-- ) /* up count loop is longer  */
{  
   wibble();
}

certains prétendent assembler pour ce cas

0x1000 ld a 0x00      'foo=0
0x1002 call 0x1234    'call wibble()
0x1005 dec a          'foo--
0x1006 cmp 0x0a       'compare foo to 10 ( like a subtract but we throw the result away)
0x1008 jrns -0x08      'jump back to 0x1000 if compare was negative
0x100a  

un peu plus c source

int foo=10;
if ( foo ) wibble()

et l'assemblée

0x1000 ld a 0x10
0x1002 jz 0x3
0x1004 call 0x1234
0x1007  

voyez comme c'est court?

un peu plus c source

int foo=10;
if ( foo==0 ) wibble()

et l'assembly (supposons un compilateur marginalement intelligent qui peut remplacer == 0 sans comparaison)

0x1000 ld a 0x10
0x1002 jz 0x3
0x1004 call 0x1234
0x1007  

Essayons maintenant une convention de true = 1

un peu plus c source #define TRUE 1 int foo = TRUE; if (foo == TRUE) wibble ()

et l'assemblée

0x1000 ld a 0x1
0x1002 cmp a 0x01
0x1004 jz 0x3
0x1006 call 0x1234
0x1009 

voyez combien le cas avec true est vrai?

Très tôt, les processeurs avaient de petits ensembles de drapeaux attachés à l'accumulateur.

Pour vérifier si a> b ou a = b prend généralement une instruction de comparaison.

  • Sauf si B est égal à ZERO - dans ce cas, l'indicateur ZERO est défini. Implémenté sous forme de NOR logique simple ou tous les bits de l'accumulateur.
  • Ou NÉGATIF ​​dans lequel il suffit d'utiliser le "bit de signe", c'est-à-dire le bit le plus significatif de l'accumulateur si vous utilisez l'arithmétique du complément à deux. (Surtout nous le faisons)

Permet de reformuler cela. Sur certains processeurs plus anciens, vous n'avez pas eu à utiliser une instruction de comparaison pour accumulateur égale à ZERO, ou un accumulateur inférieur à zéro.

Maintenant, voyez-vous pourquoi zéro pourrait être faux?

Veuillez noter que ceci est du code pseudo et qu'aucun jeu d'instructions réel ne ressemble à cela. Si vous connaissez l'assemblée, vous savez que je simplifie beaucoup les choses ici. Si vous savez quelque chose sur la conception du compilateur, vous n'avez pas besoin de lire cette réponse. Tous ceux qui connaissent le déroulage en boucle ou la prédiction de branche, la classe avancée se trouve dans la salle 203.


2
Votre remarque n’est pas bien faite ici, car d’une part, if (foo)elle if (foo != 0)devrait générer le même code et, d’autre part, vous montrez que le langage d’assemblage que vous utilisez contient en fait des opérandes et des tests booléens explicites. Par exemple jzsignifie jump if zero. En d'autres termes if (a == 0) goto target;. Et la quantité n'est même pas testée directement; la condition est convertie en un indicateur booléen qui est stocké dans un mot machine spécial. C'est en fait plus commecpu.flags.zero = (a == 0); if (cpu.flags.zero) goto target;
Kaz

Non Kaz, les anciens processeurs ne fonctionnaient pas comme ça. Le jz / jnz peut être effectué sans effectuer d’instruction de comparaison. Ce qui était vraiment le but de tout mon message.
Tim Williscroft,

2
Je n'ai rien écrit sur une instruction de comparaison.
Kaz

Pouvez-vous citer un processeur qui a une jzinstruction mais non jnz? (ou tout autre ensemble asymétrique d'instructions conditionnelles)
Toby Speight

3

Il y a beaucoup de réponses qui suggèrent qu'une correspondance entre 1 et true est rendue nécessaire par certaines propriétés mathématiques. Je ne peux pas trouver une telle propriété et suggère que c'est une convention purement historique.

Dans un champ à deux éléments, nous avons deux opérations: addition et multiplication. Nous pouvons mapper les opérations booléennes sur ce champ de deux manières:

Traditionnellement, nous identifions Vrai avec 1 et Faux avec 0. Nous identifions AND avec * et XOR avec +. Ainsi, OR est une addition saturante.

Cependant, nous pourrions tout aussi facilement identifier True avec 0 et False avec 1. Ensuite, nous identifions OR avec * et XNOR avec +. Ainsi, AND est une addition saturante.


4
Si vous aviez suivi le lien sur wikipedia, vous auriez pu constater que le concept d’une algèbre de Boole est fermé et que celui d’un champ de Galois à deux éléments ( en.wikipedia.org/wiki/GF%282%29 ). Les symboles 0 et 1 sont traditionnellement utilisés pour désigner les identités additive et multiplicative, respectivement, car les nombres réels sont également un champ dont les identités sont les nombres 0 et 1.
Giorgio

1
@NeilG Je pense que Giorgio essaie de dire que c'est plus qu'une simple convention. 0 et 1 en algèbre booléenne sont fondamentalement les mêmes que 0 et 1 en GF (2), qui se comportent presque comme 0 et 1 en nombres réels en ce qui concerne l'addition et la multiplication.
svick

1
@svick: Non, parce que vous pouvez simplement renommer la multiplication et l'addition saturante en OR, AND, puis retourner les étiquettes de sorte que 0 soit vrai et 1 vrai. Giorgio dit que c'était une convention de la logique booléenne, qui a été adoptée comme convention de l'informatique.
Neil G

1
@Neil G: Non, vous ne pouvez pas retourner + et * et 0 et 1 car un champ nécessite la distributivité de la multiplication sur l'addition (voir en.wikipedia.org/wiki/Field_%28mathematics%29 ), mais si vous définissez +: = AND et *: = XOR, vous obtenez T XOR (T ET F) = T XOR F = T, alors que (T XOR T) ET (T XOR F) = F ET T = F. Par conséquent, en retournant les opérations et les identités ne plus avoir de terrain. Ainsi, l’OMI définissant 0 et 1 comme l’identité d’un champ approprié semble capturer faux et vrais assez fidèlement.
Giorgio

1
@ giorgio: J'ai édité la réponse pour rendre évident ce qui se passe.
Neil G

3

Étrangement, zéro n'est pas toujours faux.

En particulier, les conventions Unix et Posix définissent EXIT_SUCCESS0 (et EXIT_FAILURE1). En fait, il s’agit même d’une convention C standard !

Donc, pour les shells Posix et les appels système exit (2) , 0 signifie "success", ce qui est intuitivement plus vrai que faux.

En particulier, le shell ifveut qu'un retour de processus EXIT_SUCCESS(c'est-à-dire 0) suive sa branche "then"!

Dans Scheme (mais pas dans Common Lisp ni dans MELT ) 0 et nil (c'est- ()à- dire dans Scheme) sont vrais, puisque la seule valeur fausse est#f

Je suis d'accord, je suis nerveux!


3

C est utilisé pour la programmation de bas niveau proche du matériel, domaine dans lequel vous devez parfois basculer entre les opérations au niveau du bit et les opérations logiques, sur les mêmes données. Etre obligé de convertir une expression numérique en booléen juste pour effectuer un test encombrerait le code.

Vous pouvez écrire des choses comme:

if (modemctrl & MCTRL_CD) {
   /* carrier detect is on */
}

plutôt que

if ((modemctrl & MCTRL_CD) != 0) {
    /* carrier detect is on */
}

Dans un exemple isolé, ce n’est pas si grave, mais cela sera pénible.

De même, conversez des opérations. Il est utile que le résultat d'une opération booléenne, comme une comparaison, produise simplement un 0 ou un 1: Supposons que nous voulions définir le troisième bit d'un mot en fonction de modemctrll'activation du bit de détection de porteuse:

flags |= ((modemctrl & MCTRL_CD) != 0) << 2;

Ici, nous devons avoir le != 0pour réduire le résultat de l' &expression biwise à 0ou 1, mais comme le résultat n'est qu'un entier, nous évitons d'avoir à ajouter un casting ennuyeux pour convertir davantage booléen en entier.

Bien que le C moderne ait maintenant un booltype, il conserve la validité d'un code comme celui-ci, à la fois parce que c'est une bonne chose et à cause de la rupture massive avec compatibilité ascendante qui serait autrement causée.

Un autre exemple où C est lisse: tester deux conditions booléennes en tant que commutateur à quatre directions:

switch (foo << 1 | bar) {  /* foo and bar booleans are 0 or 1 */
case 0: /* !foo && !bar */
   break;
case 1: /* !foo && bar */
   break;
case 2: /* foo && !bar */
   break;
case 3: /* foo && bar */
   break;
}

Vous ne pouvez pas enlever cela au programmeur C sans vous battre!

Enfin, C sert parfois de langage d'assemblage de haut niveau. Dans les langages d'assemblage, nous n'avons pas non plus de types booléens. Une valeur booléenne est juste un bit ou une valeur zéro par rapport à une valeur différente de zéro dans un emplacement de mémoire ou un registre. Un entier zéro, un booléen zéro et l'adresse zéro sont tous testés de la même manière dans les jeux d'instructions en langage assembleur (et peut-être même à virgule zéro). La ressemblance entre le langage C et le langage assembleur est utile, par exemple lorsque C est utilisé comme langage cible pour la compilation d'un autre langage (même celui qui a des booléens fortement typés!)


0

Une valeur booléenne ou vérité n'a que 2 valeurs. Vrai et faux.

Ceux-ci ne devraient pas être représentés sous forme d'entiers, mais sous forme de bits (0 et 1).

Dire n'importe quel autre entier à côté de 0 ou 1 n'est pas faux est une déclaration déroutante. Les tables de vérité traitent des valeurs de vérité, pas des entiers.

D'une valeur de vérité prospective, -1 ou 2 briseraient toutes les tables de vérité et toute logique booléenne associée à celles-ci.

  • 0 AND -1 ==?!
  • 0 OU 2 ==?!

La plupart des langues ont généralement un booleantype qui, lorsqu'il est converti en un type de nombre tel que entier, révèle que false doit être converti en une valeur entière égale à 0.


1
0 AND -1 == quelle que soit la valeur booléenne à laquelle vous les attribuez. Voilà ce que ma question est, pourquoi les jeter à TRUEou FALSE. Jamais je n'ai dit - peut-être je l'ai fait, mais ce n'était pas prévu - les nombres entiers étaient vrais ou faux, je leur ai demandé pourquoi ils évaluent à quel que soit le résultat lorsqu'ils sont convertis en booléens.
Morwenn

-6

En fin de compte, vous parlez de casser le langage principal car certaines API sont de mauvaise qualité. Les API Crappy ne sont pas nouvelles et vous ne pouvez pas les réparer en cassant le langage. C’est un fait mathématique que 0 est faux et 1 vrai, et toute langue qui ne le respecte pas est fondamentalement brisée. La comparaison à trois voies est un créneau et il n’ya pas d’affaire qui convertit implicitement son résultat boolcar elle renvoie trois résultats possibles. Les anciennes API C ont simplement une terrible gestion des erreurs et sont également bloquées, car C n’a pas les fonctionnalités de langage nécessaires pour ne pas avoir d’interfaces terribles.

Notez que je ne dis pas cela pour les langues qui n'ont pas de conversion implicite-> booléenne implicite.


11
"C’est un fait mathématique que 0 est faux et 1 vrai" Erm.
R. Martinho Fernandes

11
Pouvez-vous citer une référence pour votre "fait mathématique que 0 est faux et 1 est vrai"? Votre réponse ressemble dangereusement à un coup de gueule.
Dan Pichelman

14
Ce n'est pas un fait mathématique, mais c'est une convention mathématique depuis le 19ème siècle.
Charles E. Grant

1
L'algèbre booléenne est représentée par un champ fini dans lequel 0 et 1 sont les éléments d'identité pour les opérations qui ressemblent à l'addition et à la multiplication. Ces opérations sont respectivement OR et AND. En fait, l'algèbre booléenne s'écrit un peu comme l'algèbre normale où juxtaposition désigne AND et le +symbole OR. Donc, par exemple abc + a'b'csignifie (a and b and c) or (a and (not b) and (not c)).
Kaz
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.