Warum ist 0 falsch?


115

Diese Frage mag dumm klingen, aber warum werden die meisten Programmiersprachen mit einem Wert von 0 (null) und einem anderen Wert (ganzzahlig) 0bewertet ?falsetrue

String-Vergleich

Da die Frage ein bisschen zu einfach erscheint, möchte ich mich ein bisschen näher erläutern: Zunächst mag es jedem Programmierer klar erscheinen, aber warum sollte es keine Programmiersprache geben - es mag tatsächlich eine geben, aber keine Ich habe verwendet - wo wird 0ausgewertet trueund alle anderen [Ganzzahl] -Werte auf false? Diese eine Bemerkung mag zufällig erscheinen, aber ich habe ein paar Beispiele, bei denen es eine gute Idee gewesen sein könnte. Nehmen wir zunächst das Beispiel eines Strings-Drei-Wege-Vergleichs. Ich nehme C strcmpals Beispiel: Jeder Programmierer, der C als Muttersprache verwendet, könnte versucht sein, den folgenden Code zu schreiben:

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

Da strcmpgibt, 0was auswertet, falsewenn die Zeichenfolgen gleich sind, schlägt das, was der anfängliche Programmierer versucht hat, kläglich fehl und er versteht im Allgemeinen zunächst nicht, warum. Hätte stattdessen 0ausgewertet, truehätte diese Funktion in ihrem einfachsten Ausdruck verwendet werden können - dem obigen -, wenn sie auf Gleichheit verglichen worden wäre, und die richtigen Überprüfungen für -1und 1wären nur bei Bedarf durchgeführt worden. Wir hätten den Rückgabetyp die boolmeiste Zeit als (meiner Meinung nach) betrachtet.

Außerdem wollen wir einen neuen Typ einführen sign, der nur Werte annimmt -1, 0und 1. Das kann ziemlich praktisch sein. Stellen Sie sich vor, es gibt einen Raumschiff-Operator in C ++ und wir wollen ihn für std::string(nun, es gibt bereits die compareFunktion, aber der Raumschiff-Operator macht mehr Spaß). Die Erklärung wäre derzeit die folgende:

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

Wäre 0bewertet worden true, dass es den Raumschiff-Operator nicht einmal geben würde, und wir hätten das so erklären operator==können:

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

Dies operator==hätte den Drei-Wege-Vergleich sofort erledigt und könnte weiterhin verwendet werden, um die folgende Prüfung durchzuführen, während weiterhin geprüft werden kann, welche Zeichenfolge der anderen bei Bedarf lexikografisch überlegen ist:

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

Alte Fehlerbehandlung

Wir haben jetzt Ausnahmen, so dass dieser Teil nur für die alten Sprachen gilt, in denen so etwas nicht existiert (zum Beispiel C). Wenn wir uns die Standardbibliothek von C (und auch die von POSIX) ansehen, können wir mit Sicherheit feststellen, dass maaaaany-Funktionen 0bei Erfolg und jede andere Ganzzahl zurückkehren. Ich habe leider einige Leute gesehen, die solche Dinge getan haben:

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

Wenn wir darüber nachdenken, wie wir in der Programmierung denken, haben wir oft das folgende Argumentationsmuster:

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

Wenn wir noch einmal darüber nachdenken, hätte es Sinn gemacht, den einzigen neutralen Wert, 0to yes(und so funktionieren die Funktionen von C) zu setzen, während alle anderen Werte vorhanden sein können, um die vielen Fälle von zu lösen no. Ich weiß jedoch, in allen Programmiersprachen (außer vielleicht einige experimentelle esotherische Sprachen), die yesausgewertet falsein einem ifZustand, während alle noFälle auswerten zu true. Es gibt viele Situationen, in denen "es funktioniert" einen Fall darstellt, während "es funktioniert nicht" viele wahrscheinliche Ursachen darstellt. Wenn wir so darüber nachdenken , wäre es viel sinnvoller gewesen, den Rest zu 0bewerten .truefalse

Fazit

Mein Fazit ist im Wesentlichen meine ursprüngliche Frage: Warum haben wir Sprachen entworfen, wo 0ist falseund die anderen Werte sind true, unter Berücksichtigung meiner wenigen obigen Beispiele und vielleicht einiger weiterer, an die ich nicht gedacht habe?

Follow-up: Es ist schön zu sehen, dass es viele Antworten mit vielen Ideen und möglichst vielen Gründen dafür gibt. Ich finde es toll, wie leidenschaftlich du darüber zu sein scheinst. Ich habe diese Frage ursprünglich aus Langeweile gestellt, aber da Sie so leidenschaftlich zu sein scheinen, habe ich beschlossen, ein wenig weiter zu gehen und nach den Gründen für die boolesche Wahl für 0 und 1 in Math.SE zu fragen :)


32
strcmp()ist kein gutes Beispiel für wahr oder falsch, da es 3 verschiedene Werte zurückgibt. Und Sie werden überrascht sein, wenn Sie eine Shell verwenden, bei der 0 für wahr und alles andere für falsch steht.
ott--

52
@ ott--: In Unix-Shells bedeutet 0 Erfolg und Nicht-Null Misserfolg - nicht ganz dasselbe wie "wahr" und "falsch".
Keith Thompson

14
@KeithThompson: In Bash (und anderen Shells) stimmen "Erfolg" und "Misserfolg" wirklich mit "wahr" und "falsch" überein. Betrachten Sie beispielsweise die Anweisung if true ; then ... ; fi, bei der truees sich um einen Befehl handelt, der Null zurückgibt und der ifzur Ausführung auffordert ....
Ruakh

13
In Hardware gibt es keinerlei Boolesche Werte, nur Binärzahlen, und in den meisten historischen ISAs wird eine Zahl ungleich Null in allen bedingten Verzweigungsanweisungen als "wahr" betrachtet (es sei denn, sie verwenden stattdessen Flags). Die Low-Level-Sprachen sind also in jedem Fall verpflichtet, den zugrunde liegenden Hardwareeigenschaften zu folgen.
SK-logic

2
@MasonWheeler Ein Boolescher Typ impliziert nichts. Zum Beispiel Python hat einen haben boolTyp , aber Vergleiche / if Bedingungen usw. können einen beliebigen Rückgabewert haben.
Bakuriu

Antworten:


98

0ist, falseweil sie beide Null-Elemente in gemeinsamen semirings sind . Obwohl es sich um unterschiedliche Datentypen handelt, ist die Konvertierung zwischen ihnen intuitiv sinnvoll, da sie zu isomorphen algebraischen Strukturen gehören.

  • 0ist die Identität für die Addition und Null für die Multiplikation. Dies gilt für ganze und rationale Zahlen, jedoch nicht für IEEE-754-Gleitkommazahlen: 0.0 * NaN = NaNund 0.0 * Infinity = NaN.

  • falseist die Identität für Boolean xor (() und Null für Boolean und (∧). Wenn Boolesche Werte als {0, 1} dargestellt werden - die Menge der Ganzzahlen modulo 2 -, können Sie sich ⊻ als Addition ohne Übertrag und ∧ als Multiplikation vorstellen.

  • ""und []sind Identität für die Verkettung, aber es gibt mehrere Operationen, für die sie als Null sinnvoll sind. Wiederholung ist eine, aber Wiederholung und Verkettung verteilen sich nicht, sodass diese Operationen kein Semiring bilden.

Solche impliziten Konvertierungen sind in kleinen Programmen hilfreich, aber in großen Programmen kann es schwieriger sein, darüber nachzudenken. Nur einer der vielen Kompromisse beim Sprachdesign.


1
Schön, dass Sie Listen erwähnt haben. (Übrigens, nilist sowohl die leere Liste []als auch der falseWert in Common Lisp; besteht die Tendenz, Identitäten aus verschiedenen Datentypen zusammenzuführen?) Sie müssen noch erklären, warum es selbstverständlich ist, Falsch als additive Identität und Wahr als multiplikative Identität zu betrachten und nicht umgekehrt. Ist es nicht möglich, trueals das Identifizieren für ANDund Null für zu betrachten OR?
Giorgio

3
+1 für die Bezugnahme auf ähnliche Identitäten. Endlich eine Antwort, die nicht nur auf "Konvention, damit umgehen" hinausläuft.
16.

5
+1 für die Angabe von Details einer konkreten und sehr alten Mathematik, in der dies befolgt wurde und lange Sinn machte
Jimmy Hoffa

1
Diese Antwort ergibt keinen Sinn. trueist auch die Identität und die Null der Semirings (Boolean und / oder). Es gibt keinen Grund, eine Part-Konvention zu berücksichtigen, falsedie näher an 0 liegt als true.
TonioElGringo

1
@ TonioElGringo: Der Unterschied zwischen wahr und falsch ist der Unterschied zwischen XOR und XNOR. Man kann isomorphe Ringe mit AND / XOR bilden, wobei true die multiplikative Identität und false die additive Identität ist, oder mit OR und XNOR, wobei false die multiplikative Identität und true die additive Identität ist, aber XNOR wird normalerweise nicht als häufig angesehen Grundlegende Funktionsweise wie XOR.
Supercat

74

Weil die Mathematik funktioniert.

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

... insert many other examples here.

Traditionell haben C-Programme Bedingungen wie

if (someFunctionReturningANumber())

eher, als

if (someFunctionReturningANumber() != 0)

weil der Begriff der Null gleichbedeutend mit falsch ist, ist gut verstanden.


21
Die Sprachen sind so aufgebaut, weil die Mathematik Sinn macht. Das kam zuerst.
Robert Harvey

26
@Morwenn, es geht auf das 19. Jahrhundert und George Boole zurück. Leute haben Falsch als 0 und Wahr als! 0 länger dargestellt, als es Computer gegeben hat.
Charles E. Grant

11
Ich verstehe nicht, warum die Mathematik nicht anders funktioniert, wenn Sie lediglich alle Definitionen so ändern, dass AND + und OR * ist.
Neil G

7
Genau: Die Mathematik funktioniert in beide Richtungen und die Antwort auf diese Frage scheint rein konventionell zu sein.
Neil G

6
@ Robert Es wäre großartig, wenn Sie die "mathematischen Grundlagen" in Ihrem Beitrag darlegen könnten.
Phant0m

38

Wie andere gesagt haben, stand die Mathematik an erster Stelle. Deshalb ist 0 falseund 1 ist true.

Über welche Mathematik reden wir? Boolesche Algebren, die aus der Mitte des 19. Jahrhunderts stammen, lange bevor digitale Computer hinzukamen.

Man könnte auch sagen, dass die Konvention aus der Aussagenlogik hervorgegangen ist , die sogar älter ist als die Booleschen Algebren. Dies ist die Formalisierung vieler logischer Ergebnisse, die Programmierer kennen und lieben ( false || xgleich x, true && xgleich xund so weiter).

Grundsätzlich geht es um das Rechnen an einer Menge mit zwei Elementen. Denken Sie darüber nach, in Binärform zu zählen. Boolesche Algebren sind der Ursprung dieses Konzepts und seiner theoretischen Grundlage. Die Konventionen von Sprachen wie C sind nur eine einfache Anwendung.


2
Sie könnten sicher. Aber die "Standard" -Methode passt gut zur allgemeinen Arithmetik (0 + 1 = 1, nicht 0 + 1 = 0).
Joshin4colours

2
Ja, aber Sie würden vermutlich UND mit + und ODER mit * schreiben, wenn Sie auch die Definitionen umkehren würden.
Neil G

3
Die Mathematik stand nicht an erster Stelle. Math hat erkannt, dass 0 und 1 ein Feld bilden, in dem AND wie Multiplikation und OR wie Addition ist.
Kaz

1
@Kaz: Aber {0, 1} mit OR und AND bildet kein Feld.
Giorgio

2
Es stört mich ein wenig, dass mehr Antworten und Kommentare das aussagen true = 1. Das ist nicht ganz genau, denn true != 0das ist nicht genau dasselbe. Ein Grund (nicht der einzige), warum man Vergleiche wie vermeiden sollte if(something == true) { ... }.
JensG

26

Ich dachte, das hat mit der "Vererbung" von Elektronik und auch mit der Booleschen Algebra zu tun

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

strcmp gibt 0 zurück, wenn die Zeichenfolgen gleich sind. Dies hat mit seiner Implementierung zu tun, da tatsächlich der "Abstand" zwischen den beiden Zeichenfolgen berechnet wird. Dass 0 zufällig auch als falsch angesehen wird, ist nur ein Zufall.

Die Rückgabe von 0 bei Erfolg ist sinnvoll, da 0 in diesem Fall keinen Fehler bedeutet und jede andere Zahl ein Fehlercode wäre. Die Verwendung einer anderen Nummer für den Erfolg ist weniger sinnvoll, da Sie nur einen einzigen Erfolgscode haben, während Sie mehrere Fehlercodes haben können. Sie verwenden "Hat es funktioniert?" Als if-Anweisung wäre ein Ausdruck mit 0 = yes sinnvoller, aber der Ausdruck ist korrekter. "Ist etwas schief gelaufen?" und dann sehen Sie, dass 0 = nein viel Sinn macht. Denken false/truemacht hier nicht wirklich Sinn, wie es eigentlich ist no error code/error code.


Haha, Sie sind die Ersten, die die Frage nach dem Rückkehrfehler explizit gestellt haben. Ich wusste bereits, dass ich es auf meine eigene Weise interpretiert habe und es könnte auch anders lauten, aber Sie sind der erste, der es ausdrücklich ausdrückt (aus den vielen Antworten und Kommentaren). Eigentlich würde ich nicht sagen, dass der eine oder andere Weg keinen Sinn
ergibt

1
Eigentlich würde ich sagen, 0dass dies success/no errordas einzige ist, was Sinn macht, wenn andere Ganzzahlen Fehlercodes darstellen. Das ist 0auch falsein anderen Fällen nicht wirklich wichtig, da wir hier überhaupt nicht über wahr oder falsch sprechen;)
Donnerstag,

Ich hatte die gleiche Idee, also erhöhte ich
user60812

1
Ihr Standpunkt zur strcmp()Berechnung der Entfernung ist ziemlich gut. Wenn sie aufgerufen worden war , strdiff()dann if (!strdiff())wäre sehr logisch.
Kevin Cox

"Elektronik [...] mit 0 = [...] falsch, 1 = wahr" - auch in der Elektronik ist dies nur eine Konvention und nicht die einzige. Wir nennen das positive Logik, aber Sie können auch negative Logik verwenden, wobei eine positive Spannung falsch und eine negative wahr anzeigt. Dann wird die Schaltung, die Sie für AND verwenden würden, zu OR, OR zu AND und so weiter. Aufgrund des Gesetzes von De Morgan ist alles gleichwertig. Manchmal finden Sie einen Teil einer elektronischen Schaltung, die der Einfachheit halber in negativer Logik implementiert ist. An diesem Punkt werden die Namen der Signale in diesem Teil mit einem Balken darüber angegeben.
Jules

18

Wie in diesem Artikel erläutert , sollten die Werte falseund truenicht mit den Ganzzahlen 0 und 1 verwechselt werden, sondern können mit den Elementen des Galois-Feldes (endliches Feld) zweier Elemente (siehe hier ) identifiziert werden .

Ein Feld ist eine Menge mit zwei Operationen, die bestimmte Axiome erfüllen.

Die Symbole 0 und 1 werden herkömmlicherweise verwendet, um die additiven und multiplikativen Identitäten eines Feldes zu bezeichnen, da die reellen Zahlen auch ein Feld (aber kein endliches) sind, dessen Identitäten die Nummern 0 und 1 sind.

Die additive Identität ist das Element 0 des Feldes, so dass für alle x:

x + 0 = 0 + x = x

und die multiplikative Identität ist das Element 1 des Feldes, so dass für alle x:

x * 1 = 1 * x = x

Das endliche Feld zweier Elemente hat nur diese beiden Elemente, nämlich die additive Identität 0 (oder false) und die multiplikative Identität 1 (oder true). Die beiden Operationen dieses Feldes sind das logische XOR (+) und das logische AND (*).

Hinweis. Wenn Sie die Operationen umkehren (XOR ist die Multiplikation und AND ist die Addition), ist die Multiplikation nicht über die Addition verteilt und Sie haben kein Feld mehr. In einem solchen Fall haben Sie keinen Grund, die beiden Elemente 0 und 1 (in beliebiger Reihenfolge) aufzurufen. Beachten Sie auch, dass Sie nicht die Operation OR anstelle von XOR wählen können: Unabhängig davon, wie Sie OR / AND als Addition / Multiplikation interpretieren, ist die resultierende Struktur kein Feld (nicht alle inversen Elemente existieren gemäß den Feldaxiomen).

Zu den C-Funktionen:

  • Viele Funktionen geben eine Ganzzahl zurück, die ein Fehlercode ist. 0 bedeutet KEIN FEHLER.
  • Intuitiv strcmpberechnet die Funktion die Differenz zwischen zwei Zeichenfolgen. 0 bedeutet, dass zwischen zwei Strings kein Unterschied besteht, dh dass zwei Strings gleich sind.

Die obigen intuitiven Erklärungen können helfen, sich an die Interpretation der Rückgabewerte zu erinnern, aber es ist noch einfacher, nur die Bibliotheksdokumentation zu überprüfen.


1
+1 für das Zeigen, dass die Mathematik nicht mehr funktioniert, wenn Sie diese willkürlich tauschen.
Jimmy Hoffa

2
Umgedreht: Bei einem Feld mit zwei Elementen und Operationen * und + identifizieren wir True mit 0 und False mit 1. Wir identifizieren OR mit * und XOR mit +.
Neil G

1
Sie werden feststellen, dass beide Identifizierungen über dasselbe Feld erfolgen und mit den Regeln der Booleschen Logik übereinstimmen. Ihre Notiz ist leider falsch :)
Neil G

1
Wenn Sie annehmen, dass True = 0 und XOR + ist, muss True die Identität für XOR sein. Es liegt jedoch nicht daran, dass True XOR True = False ist. Es sei denn, Sie definieren die Operation XOR auf True neu, sodass True XOR True = True ist. Dann funktioniert natürlich Ihre Konstruktion, weil Sie gerade Dinge umbenannt haben (in jeder mathematischen Struktur können Sie immer erfolgreich eine Namenspermutation durchführen und eine isomorphe Struktur erhalten). Wenn Sie hingegen zulassen, dass True, False und XOR ihre übliche Bedeutung haben, kann True XOR True = False und True nicht die additive Identität sein, dh True kann nicht 0 sein.
Giorgio

1
@ Giorgio: Ich habe meine Konstruktion gemäß Ihrem Kommentar in meinem letzten Kommentar korrigiert ...
Neil G,

13

Sie sollten berücksichtigen, dass alternative Systeme auch akzeptable Entwurfsentscheidungen sein können.

Shells: 0 Exit-Status ist wahr, ungleich Null ist falsch

Das Beispiel für Shells, die den Ausgangsstatus 0 als wahr behandeln, wurde bereits erwähnt.

$ ( 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

Das Grundprinzip ist, dass es einen Weg zum Erfolg gibt, aber viele Wege zum Scheitern. Daher ist es pragmatisch, 0 als speziellen Wert zu verwenden, der "keine Fehler" bedeutet.

Ruby: 0 ist genau wie jede andere Zahl

Unter "normalen" Programmiersprachen gibt es einige Ausreißer wie Ruby, die 0 als echten Wert behandeln.

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

Das Grundprinzip ist, dass nur falseund nilsollte falsch sein. Für viele Ruby-Neulinge ist es ein Gotcha. In einigen Fällen ist es jedoch schön, dass 0 wie jede andere Zahl behandelt wird.

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"

Ein solches System funktioniert jedoch nur in einer Sprache, die Boolesche Werte als separaten Typ von Zahlen unterscheiden kann. In den früheren Tagen des Rechnens hatten Programmierer, die mit Assemblersprache oder unformatierter Maschinensprache arbeiteten, keinen solchen Luxus. Es ist wahrscheinlich nur natürlich, 0 als "leeren" Zustand zu behandeln und ein Bit auf 1 als Flag zu setzen, wenn der Code feststellt, dass etwas passiert ist. In der Folge entwickelte sich die Konvention, dass Null als falsch behandelt wurde und Nicht-Null-Werte als wahr behandelt wurden. Das muss aber nicht so sein.

Java: Zahlen können überhaupt nicht als Boolesche Werte behandelt werden

In Java trueund falsesind die einzigen booleschen Werte. Zahlen sind keine Booleschen Werte und können nicht einmal in Boolesche Werte umgewandelt werden ( Java-Sprachspezifikation, Abschnitt 4.2.2 ):

Es gibt keine Umwandlungen zwischen ganzzahligen Typen und dem Typ boolean.

Diese Regel vermeidet nur die Frage insgesamt - alle booleschen Ausdrücke müssen explizit im Code geschrieben werden.


1
Rebol und Red behandeln beide 0-wertige INTEGER! Werte als wahr und haben eine separate NONE! Typ (mit nur einem Wert, NONE) wird zusätzlich zu LOGIC als bedingt falsch behandelt! falsch. Ich habe beim Versuch, JavaScript-Code zu schreiben, der 0 als falsch behandelt, erhebliche Frustration festgestellt. es ist eine unglaublich klobige Entscheidung für eine dynamisch getippte Sprache. Wenn Sie etwas testen möchten, das null oder 0 sein kann, müssen Sie am Ende schreiben if (thing === 0), das ist einfach nicht cool.
HostileFork

@HostileFork Ich weiß es nicht. Ich finde , dass es Sinn macht , das 0ist true(wie jede andere ganze Zahl) in einer dynamischen Sprache. Ich habe manchmal einen gefangen, 0als ich versuchte, Nonein Python zu fangen , und das kann manchmal ziemlich schwer zu erkennen sein.
Morwenn

2
Ruby ist kein Ausreißer. Ruby nimmt dies von Lisp (Ruby heißt sogar heimlich "MatzLisp"). Lisp ist eine Hauptsprache in der Informatik. Zero ist auch nur ein wahrer Wert in dem POSIX - Shell, weil es ein Stück Text ist: if [ 0 ] ; then echo this executes ; fi. Der falsche Datenwert ist eine leere Zeichenfolge, und eine überprüfbare Falschheit ist ein fehlgeschlagener Beendigungsstatus eines Befehls, der durch einen Wert ungleich Null dargestellt wird.
Kaz

8

Bevor wir uns mit dem allgemeinen Fall befassen, können wir Ihre Gegenbeispiele besprechen.

String-Vergleiche

Das Gleiche gilt für viele Arten von Vergleichen. Solche Vergleiche berechnen einen Abstand zwischen zwei Objekten. Wenn die Objekte gleich sind, ist der Abstand minimal. Also , wenn der „Vergleich erfolgreich“, ist der Wert 0. Aber wirklich, von dem Rückgabewert strcmpist nicht ein boolean, es ist ein Abstand ist, und dass das, was Fallen nicht bewusst Programmierer tun if (strcmp(...)) do_when_equal() else do_when_not_equal().

In C ++ könnten wir neu designen strcmp, um ein DistanceObjekt zurückzugeben, das überschreibt operator bool(), um true zurückzugeben, wenn 0 (aber Sie würden dann von einer anderen Menge von Problemen gebissen werden). Oder haben Sie in einfachem C nur eine streqFunktion, die 1 zurückgibt, wenn Zeichenfolgen gleich sind, und sonst 0.

API-Aufrufe / Programm-Exit-Code

Hier kümmert es Sie, warum etwas schief gelaufen ist, weil dies die Entscheidungen auf Fehler treibt. Wenn die Dinge gelingen, möchten Sie nichts Bestimmtes wissen - Ihre Absicht wird verwirklicht. Der Rückgabewert muss daher diese Information übermitteln. Es ist kein Boolescher Wert, sondern ein Fehlercode. Der spezielle Fehlerwert 0 bedeutet "kein Fehler". Der Rest des Bereichs stellt lokal bedeutsame Fehler dar, mit denen Sie sich befassen müssen (einschließlich 1, was häufig "nicht spezifizierter Fehler" bedeutet).

Allgemeiner Fall

Dies wirft die Frage auf: Warum werden Boolesche Werte Trueund Falsegewöhnlich mit 1 bzw. 0 dargestellt?

Nun, neben dem subjektiven Argument "Es fühlt sich auf diese Weise besser an" gibt es hier ein paar Gründe (auch subjektiv), die mir einfallen:

  • Stromkreisanalogie. Der Strom ist für 1s EIN und für 0s AUS. Ich mag es, (1, Ja, Wahr, Ein) zusammen zu haben und (0, Nein, Falsch, Aus), anstatt eine andere Mischung

  • Speicherinitialisierungen. Wenn ich memset(0)eine Reihe von Variablen (z. B. Ints, Floats, Bools) habe, möchte ich, dass ihr Wert den konservativsten Annahmen entspricht. ZB ist meine Summe anfänglich 0, das Prädikat ist Falsch usw.

Vielleicht hängen all diese Gründe mit meiner Ausbildung zusammen - wenn mir von Anfang an beigebracht worden wäre, 0 mit True zu assoziieren, würde ich den umgekehrten Weg einschlagen.


2
Tatsächlich gibt es mindestens eine Programmiersprache, die 0 als wahr behandelt. Die Unix-Shell.
Jan Hudec

+1 für die Lösung des eigentlichen Problems: Die meisten Fragen von Morwenn haben überhaupt nichts zu tun bool.
Dan04

@ dan04 Es ist. In dem gesamten Beitrag geht es um die Gründe für die Auswahl der Besetzung von intbis boolin vielen Programmiersprachen. Das Vergleichs- und Fehlergestenmaterial ist nur ein Beispiel für Orte, an denen es sinnvoll gewesen wäre, es auf eine andere Weise als derzeit auszuführen.
Morwenn

6

Aus einer übergeordneten Perspektive sprechen Sie von drei recht unterschiedlichen Datentypen:

  1. Ein Boolescher. Die mathematische Konvention in der Booleschen Algebra lautet 0 für falseund 1 für true, daher ist es sinnvoll, dieser Konvention zu folgen. Ich denke, dass dieser Weg auch intuitiver Sinn macht.

  2. Das Ergebnis des Vergleichs. Dies hat drei Werte: <, =und >( man beachte , dass keiner von ihnen ist true). Für sie ist es sinnvoll, die Werte -1, 0 bzw. 1 (oder allgemeiner einen negativen Wert, Null und einen positiven Wert) zu verwenden.

    Wenn Sie auf Gleichheit prüfen möchten und nur eine Funktion haben, die einen allgemeinen Vergleich durchführt, sollten Sie dies meines Erachtens mit etwas wie explizit machen strcmp(str1, str2) == 0. Ich finde die Verwendung !in dieser Situation verwirrend, da ein nicht-boolescher Wert so behandelt wird, als wäre er ein boolescher Wert.

    Denken Sie auch daran, dass Vergleich und Gleichheit nicht dasselbe sein müssen. Wenn Sie beispielsweise Personen nach ihrem Geburtsdatum bestellen, Compare(me, myTwin)sollten Sie zurückkehren 0, Equals(me, myTwin)sollten aber zurückkehren false.

  3. Der Erfolg oder Misserfolg einer Funktion, möglicherweise auch mit Details zu diesem Erfolg oder Misserfolg. Wenn Sie über Windows sprechen, wird dieser Typ aufgerufen, HRESULTund ein Wert ungleich Null zeigt nicht unbedingt einen Fehler an. In der Tat bedeutet ein negativer Wert ein Scheitern und einen nicht negativen Erfolg. Der Erfolgswert ist sehr häufig S_OK = 0, es kann sich aber auch beispielsweise um einen S_FALSE = 1anderen Wert handeln.

Die Verwirrung ergibt sich aus der Tatsache, dass drei logisch sehr unterschiedliche Datentypen in C und einigen anderen Sprachen als ein einziger Datentyp (eine Ganzzahl) dargestellt werden und dass Sie in einer Bedingung eine Ganzzahl verwenden können. Ich halte es jedoch nicht für sinnvoll, Boolesche Werte neu zu definieren, um die Verwendung einiger nicht-boolescher Typen unter einfacheren Bedingungen zu vereinfachen.

Denken Sie auch an einen anderen Typ, der in einer Bedingung in C häufig verwendet wird: einen Zeiger. Dort ist es natürlich, einen NULLZeiger (der als dargestellt wird 0) als zu behandeln false. Wenn Sie also Ihrem Vorschlag folgen, wird das Arbeiten mit Zeigern ebenfalls schwieriger. (Ich persönlich bevorzuge jedoch den expliziten Vergleich von Zeigern mit Zeigern NULL, anstatt sie als Boolesche Werte zu behandeln.)


4

Null kann falsch sein, da die meisten CPUs ein ZERO-Flag haben, das zum Verzweigen verwendet werden kann. Es wird eine Vergleichsoperation gespeichert.

Mal sehen warum.

Einige Pseudocodes, da das Publikum Assembler wahrscheinlich nicht liest

c-source simple loop calls wibbeln 10 mal

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

Einige tun so, als würden sie sich versammeln

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 ruft eine andere einfache Schleife 10 Mal wibble auf

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

einige so tun, als Montage für diesen Fall

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  

etwas mehr c Quelle

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

und die Versammlung

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

Mal sehen, wie kurz das ist?

etwas mehr c Quelle

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

und die Assembly (nehmen wir einen marginal intelligenten Compiler an, der == 0 ohne Vergleich ersetzen kann)

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

Versuchen wir nun eine Konvention von true = 1

etwas mehr c source #define TRUE 1 int foo = TRUE; if (foo == TRUE) wibble ()

und die Versammlung

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

sehen, wie kurz der Fall mit ungleich Null wahr ist?

Wirklich frühe CPUs hatten kleine Flaggensätze am Akkumulator.

Um zu überprüfen, ob a> b oder a = b ist, wird im Allgemeinen eine Vergleichsanweisung verwendet.

  • Es sei denn, B ist entweder NULL - in diesem Fall wird das ZERO-Flag als einfaches logisches NOR implementiert oder alle Bits im Akkumulator.
  • Oder NEGATIV, bei dem Sie nur das "Vorzeichenbit" verwenden, dh das höchstwertige Bit des Akkumulators, wenn Sie die Zweierkomplementarithmetik verwenden. (Meistens tun wir das)

Lassen Sie uns das noch einmal wiederholen. Auf einigen älteren CPUs mussten Sie keinen Vergleichsbefehl für einen Akku gleich NULL oder einen Akku kleiner als Null verwenden.

Sehen Sie jetzt, warum Null falsch sein könnte?

Bitte beachten Sie, dass dies ein Pseudocode ist und kein richtiger Befehlssatz so aussieht. Wenn Sie Assembler kennen, wissen Sie, dass ich hier vieles vereinfache. Wenn Sie etwas über das Compiler-Design wissen, mussten Sie diese Antwort nicht lesen. Jeder, der etwas über das Abrollen von Schleifen oder die Vorhersage von Verzweigungen weiß, ist in der fortgeschrittenen Klasse in Raum 203 im Flur.


2
Ihr Punkt ist nicht gut hier gemacht , weil für eine Sache if (foo)und if (foo != 0)sollte den gleichen Code erzeugen, und zweitens, Sie zeigen , dass die Assemblersprache Sie in der Tat mit bist hat explizite boolean Operanden und Tests für sie. Zum Beispiel jzbedeutet jump if zero. Mit anderen Worten if (a == 0) goto target;. Und die Menge wird nicht einmal direkt getestet; Die Bedingung wird in ein Boolesches Flag umgewandelt, das in einem speziellen Maschinenwort gespeichert ist. Es ist eigentlich eher wiecpu.flags.zero = (a == 0); if (cpu.flags.zero) goto target;
Kaz

Nein Kaz, die älteren CPUs haben so nicht funktioniert. Das jz / jnz kann ohne eine Vergleichsanweisung ausgeführt werden. Was war eigentlich der Sinn meines ganzen Beitrags?
Tim Williscroft

2
Ich habe nichts über eine Vergleichsanweisung geschrieben.
Kaz

Können Sie einen Prozessor nennen, der eine jzAnweisung hat, aber keine jnz? (oder eine andere asymmetrische Reihe von bedingten Anweisungen)
Toby Speight

3

Es gibt viele Antworten, die darauf hindeuten, dass die Übereinstimmung zwischen 1 und wahr durch eine mathematische Eigenschaft erforderlich ist. Ich kann keine solche Immobilie finden und schlage vor, dass es sich um eine rein historische Konvention handelt.

Bei einem Feld mit zwei Elementen haben wir zwei Operationen: Addition und Multiplikation. Wir können boolesche Operationen in diesem Feld auf zwei Arten abbilden:

Traditionell identifizieren wir True mit 1 und False mit 0. Wir identifizieren AND mit * und XOR mit +. Somit ist OR eine Sättigungsaddition.

Genauso einfach können wir jedoch True mit 0 und False mit 1 identifizieren. Dann identifizieren wir OR mit * und XNOR mit +. Somit ist AND eine Sättigungsaddition.


4
Wenn Sie dem Link auf Wikipedia gefolgt wären, hätten Sie feststellen können, dass das Konzept einer Booleschen Algebra mit dem eines Galois-Feldes mit zwei Elementen in Beziehung steht ( en.wikipedia.org/wiki/GF%282%29 ). Die Symbole 0 und 1 werden herkömmlicherweise verwendet, um die additive bzw. multiplikative Identität zu bezeichnen, da die reellen Zahlen auch ein Feld sind, dessen Identitäten die Nummern 0 und 1 sind.
Giorgio

1
@NeilG Ich denke, Giorgio versucht zu sagen, dass es mehr als nur eine Konvention ist. 0 und 1 in der Booleschen Algebra sind grundsätzlich die gleichen wie 0 und 1 in GF (2), die sich hinsichtlich Addition und Multiplikation in reellen Zahlen fast wie 0 und 1 verhalten.
SVICK

1
@svick: Nein, denn Sie können Multiplikation und Sättigungsaddition einfach in OR und AND umbenennen und dann die Bezeichnungen umdrehen, sodass 0 True und 1 False ist. Giorgio sagt, dass es eine Konvention der Booleschen Logik war, die als Konvention der Informatik übernommen wurde.
Neil G

1
@Neil G: Nein, Sie können + und * sowie 0 und 1 nicht spiegeln, da für ein Feld eine Verteilung der Multiplikation über die Addition erforderlich ist (siehe en.wikipedia.org/wiki/Field_%28mathematics%29 ) und *: = XOR, erhalten Sie T XOR (T UND F) = T XOR F = T, während (T XOR T) UND (T XOR F) = F UND T = F sind habe kein Feld mehr. Daher scheint die IMO, die 0 und 1 als die Identitäten eines geeigneten Feldes definiert, falsch und wahr ziemlich genau zu erfassen.
Giorgio

1
@giorgio: Ich habe die Antwort bearbeitet, um zu verdeutlichen, was los ist.
Neil G

3

Merkwürdigerweise ist Null nicht immer falsch.

Insbesondere ist die Unix- und Posix-Konvention EXIT_SUCCESSals 0 (und EXIT_FAILUREals 1) zu definieren . Eigentlich ist es sogar eine Standard- C-Konvention !

Für Posix-Shells und exit (2) -Syscalls bedeutet 0 also "erfolgreich", was intuitiv eher wahr als falsch ist.

Insbesondere ifmöchte die Shell, dass eine Prozessrückgabe EXIT_SUCCESS(dh 0) der "then" -Verzweigung folgt!

In Schema (aber nicht in Common Lisp oder in MELT ) sind 0 und nil (dh ()in Schema) wahr, da der einzige falsche Wert ist#f

Ich bin damit einverstanden, ich nicke!


3

C wird für die Low-Level-Programmierung in der Nähe der Hardware verwendet. In diesem Bereich müssen Sie manchmal zwischen bitweisen und logischen Operationen für dieselben Daten wechseln. Wenn Sie einen numerischen Ausdruck nur zur Durchführung eines Tests in einen booleschen Ausdruck konvertieren müssen, wird der Code unübersichtlich.

Sie können Dinge schreiben wie:

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

eher, als

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

In einem isolierten Beispiel ist es nicht so schlimm, aber das zu tun wird lästig.

Ebenso umgekehrte Operationen. Es ist nützlich, wenn das Ergebnis einer booleschen Operation wie ein Vergleich nur eine 0 oder 1 ergibt: Angenommen, wir möchten das dritte Bit eines Wortes basierend darauf setzen, ob modemctrlder Träger das Bit erkennt:

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

Hier müssen wir das, haben != 0, um das Ergebnis des biweisen &Ausdrucks auf 0oder zu reduzieren 1, aber da das Ergebnis nur eine Ganzzahl ist, müssen wir keine lästigen Casts hinzufügen, um Boolean in Ganzzahl umzuwandeln.

Obwohl modernes C jetzt einen boolTyp hat, behält es die Gültigkeit von Code wie diesem bei, sowohl weil es eine gute Sache ist, als auch wegen des massiven Bruchs mit der Abwärtskompatibilität, der sonst verursacht würde.

Ein weiteres Beispiel, in dem C glatt ist: Testen von zwei Booleschen Bedingungen als Vier-Wege-Schalter:

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;
}

Sie könnten dies dem C-Programmierer nicht ohne einen Kampf nehmen!

Schließlich dient C manchmal als eine Art Assemblersprache auf hoher Ebene. In Assemblersprachen gibt es auch keine Booleschen Typen. Ein Boolescher Wert ist nur ein Bit oder ein Wert von Null gegen einen Wert ungleich Null in einem Speicherort oder Register. Eine Ganzzahl Null, eine Boolesche Null und die Adresse Null werden in Assembler-Befehlssätzen (und möglicherweise sogar Gleitkomma Null) auf dieselbe Weise getestet. Die Ähnlichkeit zwischen C und Assembler-Sprache ist nützlich, wenn beispielsweise C als Zielsprache für die Kompilierung einer anderen Sprache verwendet wird (auch einer, die stark typisierte Boolesche Sprachen hat!).


0

Ein Boolescher Wert oder Wahrheitswert hat nur 2 Werte. Richtig und falsch.

Diese sollten nicht als ganze Zahlen dargestellt werden, sondern als Bits (0 und 1).

Es ist verwirrend zu sagen, dass eine andere Ganzzahl neben 0 oder 1 nicht falsch ist. Wahrheitstabellen befassen sich mit Wahrheitswerten, nicht mit ganzen Zahlen.

Ausgehend von einer prospektiven Wahrheitswertigkeit würde -1 oder 2 alle Wahrheitstabellen und jede damit verbundene boolesche Logik brechen.

  • 0 UND -1 ==?!
  • 0 ODER 2 ==?!

Die meisten Sprachen haben normalerweise einen booleanTyp, der, wenn er in einen Zahlentyp wie eine Ganzzahl umgewandelt wird, false als Ganzzahlwert 0 ausgibt.


1
0 UND -1 == Welchen Booleschen Wert Sie auch immer verwenden. Darum geht es in meiner Frage, warum sie an TRUEoder geworfen werden FALSE. Niemals habe ich gesagt - vielleicht habe ich es getan, aber es war nicht beabsichtigt - Ganzzahlen waren wahr oder falsch. Ich fragte, warum sie bei der Umwandlung in einen Booleschen Wert für den jeweiligen Wert ausgewertet werden.
Morwenn

-6

Letztendlich geht es darum, die Kernsprache zu brechen, weil einige APIs beschissen sind. Crappy APIs sind nicht neu, und Sie können sie nicht beheben, indem Sie die Sprache brechen. Es ist eine mathematische Tatsache, dass 0 falsch und 1 wahr ist, und jede Sprache, die dies nicht beachtet, ist grundsätzlich gebrochen. Der Drei-Wege-Vergleich ist eine Nische und hat nichts damit zu tun, dass das Ergebnis implizit in konvertiert wird, boolda drei mögliche Ergebnisse zurückgegeben werden. Die alten C-APIs haben einfach eine schreckliche Fehlerbehandlung und sind auch überfordert, da C nicht über die erforderlichen Sprachfunktionen verfügt, um keine schrecklichen Schnittstellen zu haben.

Beachten Sie, dass ich das nicht für Sprachen sage, die keine implizite Ganzzahl-> Boolesche Konvertierung haben.


11
"Es ist eine mathematische Tatsache, dass 0 falsch und 1 wahr ist" Ähm.
R. Martinho Fernandes

11
Können Sie eine Referenz für Ihre "mathematische Tatsache, dass 0 falsch und 1 wahr ist" zitieren? Ihre Antwort klingt gefährlich wie ein Schimpfen.
Dan Pichelman

14
Es ist keine mathematische Tatsache, aber seit dem 19. Jahrhundert eine mathematische Konvention.
Charles E. Grant

1
Die Boolesche Algebra wird durch ein endliches Feld dargestellt, in dem 0 und 1 die Identitätselemente für Operationen sind, die Addition und Multiplikation ähneln. Diese Operationen sind ODER und UND. Tatsächlich wird die Boolesche Algebra ähnlich wie die normale Algebra geschrieben, bei der die Nebeneinanderstellung AND und das +Symbol OR bezeichnet. Also zum Beispiel abc + a'b'cbedeutet (a and b and c) or (a and (not b) and (not c)).
Kaz
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.