Perché 0 è falso?


115

Questa domanda può sembrare stupida, ma perché 0valuta falsee ogni altro valore [intero] trueè la maggior parte dei linguaggi di programmazione?

Confronto delle stringhe

Dal momento che la domanda sembra un po 'troppo semplice, mi spiego un po' di più: prima di tutto, può sembrare evidente a qualsiasi programmatore, ma perché non ci dovrebbe essere un linguaggio di programmazione - in realtà potrebbe esserci, ma nessuno Ho usato - dove 0valuta truee tutti gli altri valori [interi] false? Quell'osservazione può sembrare casuale, ma ho alcuni esempi in cui potrebbe essere stata una buona idea. Prima di tutto, prendiamo l'esempio del confronto a tre stringhe, prenderò l'esempio di C strcmp: qualsiasi programmatore che prova C come prima lingua potrebbe essere tentato di scrivere il seguente codice:

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

Dato che strcmpritorna 0che valuta falsequando le stringhe sono uguali, ciò che il programmatore principiante ha tentato di fare fallisce miseramente e generalmente non capisce perché all'inizio. Avendo 0valutato trueinvece, questa funzione avrebbe potuto essere usata nella sua espressione più semplice - quella sopra - quando si confrontava per l'uguaglianza, e i controlli adeguati per -1e 1sarebbero stati fatti solo quando necessario. Avremmo considerato il tipo di ritorno come bool(nella nostra mente intendo) il più delle volte.

Inoltre, cerchiamo di introdurre un nuovo tipo, signche vuole solo i valori -1, 0e 1. Questo può essere molto utile. Immagina che ci sia un operatore di astronave in C ++ e lo vogliamo std::string(beh, c'è già la comparefunzione, ma l'operatore di astronave è più divertente). La dichiarazione sarebbe attualmente la seguente:

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

Era 0stato valutato true, l'operatore dell'astronave non esisterebbe, e avremmo potuto dichiararlo in operator==questo modo:

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

Ciò operator==avrebbe gestito il confronto a tre vie contemporaneamente e potrebbe ancora essere utilizzato per eseguire il seguente controllo pur essendo in grado di verificare quale stringa è lessicograficamente superiore all'altra quando necessario:

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

Gestione degli errori precedenti

Ora abbiamo delle eccezioni, quindi questa parte si applica solo alle vecchie lingue in cui non esiste nulla del genere (ad esempio C). Se guardiamo alla libreria standard di C (e anche a POSIX), possiamo vedere con certezza che le funzioni maaaaany ritornano 0quando hanno successo e qualsiasi numero intero altrimenti. Purtroppo ho visto alcune persone fare questo tipo di cose:

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

Se pensiamo a come pensiamo in programmazione, spesso abbiamo il seguente modello di ragionamento:

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

Se ci pensiamo di nuovo, avrebbe avuto senso mettere l'unico valore neutro,, 0a yes(ed è così che funzionano le funzioni di C), mentre tutti gli altri valori possono essere lì per risolvere i molti casi di no. Tuttavia, in tutti i linguaggi di programmazione che conosco (tranne forse alcuni linguaggi esoterici sperimentali), che yesvaluta falsein una ifcondizione, mentre tutti i nocasi valutano true. Ci sono molte situazioni in cui "funziona" rappresenta un caso mentre "non funziona" rappresenta molte probabili cause. Se ci pensiamo in questo modo, avere 0valutato truee il resto falseavrebbe avuto molto più senso.

Conclusione

La mia conclusione è essenzialmente la mia domanda originale: perché abbiamo progettato linguaggi dove 0sono falsee gli altri valori true, tenendo conto dei miei pochi esempi sopra e forse qualche altro a cui non ho pensato?

Follow-up: è bello vedere che ci sono molte risposte con molte idee e quante più ragioni possibili perché sia ​​così. Adoro quanto ti sembri appassionato. Ho originariamente fatto questa domanda per noia, ma poiché sembri così appassionato, ho deciso di andare un po 'oltre e chiedere la logica alla base della scelta booleana per 0 e 1 su Math.SE :)


32
strcmp()non è un buon esempio di vero o falso, in quanto restituisce 3 valori diversi. E rimarrai sorpreso quando inizi a usare una shell, dove 0 significa vero e qualsiasi altra cosa significa falso.
ott--

52
@ ott--: nelle shell Unix, 0 significa successo e diverso da zero significa fallimento - non esattamente la stessa cosa di "vero" e "falso".
Keith Thompson,

14
@KeithThompson: in Bash (e altre shell), "successo" e "fallimento" sono davvero gli stessi di "vero" e "falso". Si consideri, ad esempio, l'istruzione if true ; then ... ; fi, in cui trueè un comando che restituisce zero e che indica ifl'esecuzione ....
Ruakh,

13
Non ci sono booleani nell'hardware, solo numeri binari e nella maggior parte degli ISA storici un numero diverso da zero è considerato "vero" in tutte le istruzioni di diramazione condizionale (a meno che non utilizzino invece flag). Pertanto, le lingue di basso livello sono obbligate a seguire le proprietà hardware sottostanti.
SK-logic,

2
@MasonWheeler Avere un tipo booleano non implica nulla. Per esempio python ha un booltipo, ma se le condizioni ecc possono avere qualsiasi valore il confronto / ritorno.
Bakuriu,

Risposte:


98

0è falseperché sono entrambi zero elementi nelle semirazioni comuni . Anche se sono tipi di dati distinti, ha senso intuitivo convertirsi tra loro perché appartengono a strutture algebriche isomorfe.

  • 0è l'identità per l'addizione e zero per la moltiplicazione. Questo è vero per numeri interi e razionali, ma non per i numeri in virgola mobile IEEE-754: 0.0 * NaN = NaNe 0.0 * Infinity = NaN.

  • falseè l'identità per Boorean xor (⊻) e zero per Boolean e (∧). Se i booleani sono rappresentati come {0, 1}, l'insieme di numeri interi modulo 2, puoi pensare a ⊻ come addizione senza carry e ∧ come moltiplicazione.

  • ""e []sono identità per la concatenazione, ma ci sono diverse operazioni per le quali hanno senso come zero. La ripetizione è una, ma la ripetizione e la concatenazione non si distribuiscono, quindi queste operazioni non formano un semiring.

Tali conversioni implicite sono utili in piccoli programmi, ma in generale possono rendere i programmi più difficili da ragionare. Solo uno dei tanti compromessi nella progettazione del linguaggio.


1
Bello che tu abbia menzionato le liste. (A proposito, nilè sia la lista vuota []che il falsevalore in Common Lisp; c'è una tendenza a fondere identità da diversi tipi di dati?) Devi ancora spiegare perché è naturale considerare false come identità additiva e true come identità moltiplicativa e non viceversa. Non è possibile considerare truel'identificativo per ANDe zero per OR?
Giorgio,

3
+1 per fare riferimento a identità simili. Finalmente una risposta che non si limita a "convenzione, affrontarla".
l0b0

5
+1 per i dettagli di una matematica concreta e molto antica in cui questa è stata seguita e ha avuto senso per molto tempo
Jimmy Hoffa,

1
Questa risposta non ha senso. trueè anche l'identità e lo zero dei semirings (booleani e / o). Non vi è alcun motivo, a parte la convenzione, da considerare falseè più vicino a 0 di true.
TonioElGringo,

1
@TonioElGringo: la differenza tra vero e falso è la differenza tra XOR e XNOR. Si possono formare anelli isomorfi usando AND / XOR, dove vero è l'identità moltiplicativa e falso quello additivo, o con OR e XNOR, dove falso è l'identità moltiplicativa e vero è quello additivo, ma XNOR di solito non è considerato comune operazione fondamentale com'è XOR.
supercat

74

Perché la matematica funziona.

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

... insert many other examples here.

Tradizionalmente, i programmi C hanno condizioni simili

if (someFunctionReturningANumber())

piuttosto che

if (someFunctionReturningANumber() != 0)

perché il concetto di zero equivale a falso è ben compreso.


21
Le lingue sono progettate in questo modo perché la matematica ha un senso. Quello è venuto prima.
Robert Harvey,

26
@Morwenn, risale al 19 ° secolo e George Boole. Le persone rappresentano False come 0 e True come! 0 da più tempo di quanto non ci siano stati i computer.
Charles E. Grant,

11
Non vedo perché la matematica non funzioni nell'altro modo se cambi semplicemente tutte le definizioni in modo che AND sia + e OR sia *.
Neil G,

7
Esatto: la matematica funziona in entrambi i modi e la risposta a questa domanda sembra essere puramente convenzionale.
Neil G,

6
@Robert Sarebbe bello se potessi precisare le "basi matematiche" nel tuo post.
phant0m

38

Come altri hanno già detto, la matematica è arrivata per prima. Questo è il motivo per cui 0 è falsee 1 è true.

Di quale matematica stiamo parlando? Algebre booleane che risalgono alla metà del 1800, molto prima che arrivassero i computer digitali.

Si potrebbe anche dire che la convenzione è nata dalla logica proposizionale , che è ancora più antica delle algebre booleane. Questa è la formalizzazione di molti dei risultati logici che i programmatori conoscono e amano ( false || xuguali x, true && xuguali xe così via).

Fondamentalmente stiamo parlando dell'aritmetica su un set con due elementi. Pensa a contare in binario. Le algebre booleane sono l'origine di questo concetto e la sua base teorica. Le convenzioni di linguaggi come C sono solo una semplice applicazione.


2
Potresti, di sicuro. Ma tenerlo nel modo "standard" si adatta bene all'aritmetica generale (0 + 1 = 1, non 0 + 1 = 0).
joshin4colours,

2
Sì, ma presumibilmente scriveresti AND con + e OR con * se invertissi anche le definizioni.
Neil G,

3
La matematica non è venuta prima. La matematica ha riconosciuto che 0 e 1 formano un campo, in cui AND è come una moltiplicazione e OR è come un'aggiunta.
Kaz,

1
@Kaz: Ma {0, 1} con OR e AND non formano un campo.
Giorgio,

2
Mi dà un po 'fastidio il fatto che più risposte e commenti lo affermino true = 1. Non è esattamente esatto, perché true != 0non è esattamente lo stesso. Una ragione (non l'unica) per cui si dovrebbero evitare confronti simili if(something == true) { ... }.
JensG,

26

Pensavo che ciò avesse a che fare con l '"eredità" dell'elettronica e anche con l'algebra booleana, dove

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

strcmp restituisce 0 quando le stringhe sono uguali ha a che fare con la sua implementazione, poiché ciò che effettivamente fa è calcolare la "distanza" tra le due stringhe. Che anche 0 sia considerato falso è solo una coincidenza.

restituire 0 in caso di successo ha senso perché 0 in questo caso viene utilizzato per indicare nessun errore e qualsiasi altro numero sarebbe un codice di errore. L'uso di qualsiasi altro numero per il successo avrebbe meno senso dato che hai un solo codice di successo, mentre puoi avere diversi codici di errore. Usi "Ha funzionato?" poiché l'espressione dell'istruzione if e dire 0 = yes avrebbe più senso, ma l'espressione è più corretta "Qualcosa è andato storto?" e poi vedi che 0 = no ha molto senso. Pensare false/truenon ha davvero senso qui, come in realtà no error code/error code.


Haha, sei il primo a dichiarare esplicitamente la domanda di errore di ritorno. Sapevo già di averlo interpretato a modo mio e che avrebbe potuto essere chiesto nell'altro modo, ma tu sei il primo a esprimerlo esplicitamente (tra le tante risposte e commenti). In realtà, non direi che l'uno o l'altro modo non ha senso, ma più che entrambi hanno senso in modi diversi :)
Morwenn,

1
In realtà direi che 0per success/no errorè l'unica cosa che ha senso quando gli altri numeri interi rappresentano codici di errore. Ciò 0accade anche per rappresentare falsein altri casi, non importa, dal momento che qui non stiamo parlando di vero o falso;)
Svish,

Ho avuto la stessa idea, quindi ho
sollevato

1
Il tuo punto sul strcmp()calcolo della distanza è abbastanza buono. Se fosse stato chiamato strdiff()allora if (!strdiff())sarebbe molto logico.
Kevin Cox,

"elettronica [...] dove 0 = [...] falso, 1 = [...] vero" - anche in elettronica, questa è solo una convenzione e non è l'unica. Chiamiamo questa logica positiva, ma puoi anche usare la logica negativa, dove una tensione positiva indica falso e negativo indica vero. Quindi, il circuito che useresti per AND diventa OR, OR diventa AND e così via. A causa della legge di De Morgan, tutto finisce per essere equivalente. A volte, troverai una parte di un circuito elettronico implementato in logica negativa per comodità, a quel punto i nomi dei segnali in quella parte sono annotati con una barra sopra di essi.
Jules,

18

Come spiegato in questo articolo , i valori falsee truenon devono essere confusi con gli interi 0 e 1, ma possono essere identificati con gli elementi del campo Galois (campo finito) di due elementi (vedi qui ).

Un campo è un insieme con due operazioni che soddisfano determinati assiomi.

I simboli 0 e 1 sono usati convenzionalmente per indicare le identità additive e moltiplicative di un campo perché anche i numeri reali sono un campo (ma non uno finito) le cui identità sono i numeri 0 e 1.

L'identità additiva è l'elemento 0 del campo, tale che per tutti x:

x + 0 = 0 + x = x

e l'identità moltiplicativa è l'elemento 1 del campo, tale che per tutti x:

x * 1 = 1 * x = x

Il campo finito di due elementi ha solo questi due elementi, ovvero l'identità additiva 0 (o false) e l'identità moltiplicativa 1 (o true). Le due operazioni di questo campo sono XOR logico (+) e AND logico (*).

Nota. Se si invertono le operazioni (XOR è la moltiplicazione e AND è l'aggiunta), la moltiplicazione non è distributiva rispetto all'aggiunta e non si ha più un campo. In tal caso non hai motivo di chiamare i due elementi 0 e 1 (in qualsiasi ordine). Si noti inoltre che non è possibile scegliere l'operazione OR invece di XOR: indipendentemente da come interpretate OR / AND come addizione / moltiplicazione, la struttura risultante non è un campo (non tutti gli elementi inversi esistono come richiesto dagli assiomi del campo).

Per quanto riguarda le funzioni C:

  • Molte funzioni restituiscono un numero intero che è un codice di errore. 0 significa NESSUN ERRORE.
  • Intuitivamente, la funzione strcmpcalcola la differenza tra due stringhe. 0 significa che non c'è differenza tra due stringhe, ovvero che due stringhe sono uguali.

Le spiegazioni intuitive sopra riportate possono aiutare a ricordare l'interpretazione dei valori restituiti, ma è ancora più semplice controllare la documentazione della libreria.


1
+1 per aver dimostrato che se li scambi arbitrariamente, la matematica non risolve più.
Jimmy Hoffa,

2
Capovolto: dato un campo con due elementi e operazioni * e +, identifichiamo True con 0 e False con 1. Identifichiamo OR con * e XOR con +.
Neil G,

1
Scoprirai che entrambe queste identificazioni vengono eseguite nello stesso campo ed entrambe sono coerenti con le regole della logica booleana. La tua nota è purtroppo errata :)
Neil G

1
Se supponi che True = 0 e XOR sia +, True deve essere l'identità di XOR. Ma non è perché True XOR True = False. A meno che non si ridefinisca l'operazione XOR su True in modo che True XOR True = True. Quindi ovviamente la tua costruzione funziona perché hai appena rinominato le cose (in qualsiasi struttura matematica puoi sempre fare con successo una permutazione del nome e ottenere una struttura isomorfa). D'altra parte, se lasci che True, False e XOR abbiano il loro solito significato, allora True XOR True = False e True non può essere l'identità additiva, cioè True non può essere 0.
Giorgio,

1
@Giorgio: ho corretto la mia costruzione per il tuo commento nel mio ultimo commento ...
Neil G

13

È necessario considerare che anche i sistemi alternativi possono essere decisioni di progettazione accettabili.

Shell: 0 lo stato di uscita è vero, diverso da zero è falso

L'esempio delle shell che trattano uno stato di uscita 0 come vero è già stato menzionato.

$ ( 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 logica è che esiste un modo per avere successo, ma molti modi per fallire, quindi usare 0 come valore speciale che significa "nessun errore" è pragmatico.

Ruby: 0 è proprio come qualsiasi altro numero

Tra i linguaggi di programmazione "normali", ci sono alcuni valori anomali, come Ruby, che trattano lo 0 come un valore reale.

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

La logica è che solo falsee nildovrebbe essere falsa. Per molti novizi di Ruby, è un gotcha. Tuttavia, in alcuni casi, è bello che 0 sia trattato come qualsiasi altro numero.

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"

Tuttavia, tale sistema funziona solo in una lingua che è in grado di distinguere i booleani come un tipo separato dai numeri. Nei primi tempi dell'informatica, i programmatori che lavoravano con il linguaggio assembly o il linguaggio macchina grezzo non avevano tali lussi. Probabilmente è naturale considerare 0 come stato "vuoto" e impostare un po 'a 1 come flag quando il codice rileva che è successo qualcosa. Per estensione, la convenzione ha sviluppato che zero è stato trattato come falso e valori diversi da zero sono stati trattati come veri. Tuttavia, non deve essere così.

Java: i numeri non possono essere trattati come booleani

In Java, truee falsesono gli unici valori booleani. I numeri non sono booleani e non possono nemmeno essere convertiti in booleani ( Java Language Specification, Sec 4.2.2 ):

Non ci sono cast tra i tipi integrali e il tipo boolean.

Questa regola evita del tutto la domanda: tutte le espressioni booleane devono essere esplicitamente scritte nel codice.


1
Rebol e Red trattano entrambi INTEGER con valore 0! valori come veri e hanno un NONE separato! tipo (con un solo valore, NONE) trattato come falso condizionale oltre a LOGIC! falsa. Ho riscontrato una notevole frustrazione nel tentativo di scrivere codice JavaScript che considera 0 falso; è una decisione incredibilmente ingombrante per un linguaggio tipizzato in modo dinamico. Se vuoi testare qualcosa che può essere nullo o 0 finisci per dover scrivere if (thing === 0), non è bello.
HostileFork,

@HostileFork Non lo so. Trovo che ha senso che 0sia true(come ogni altro intero) in un linguaggio dinamico. A volte mi è capitato di catturare un 0quando cercavo di catturare Nonein Python, e questo a volte può essere piuttosto difficile da individuare.
Morwenn,

2
Ruby non è un outlier. Ruby lo prende da Lisp (Ruby è anche segretamente chiamato "MatzLisp"). Lisp è un linguaggio tradizionale nell'informatica. Zero è anche solo un vero valore nella shell POSIX, perché è un pezzo di testo: if [ 0 ] ; then echo this executes ; fi. Il valore di dati falsi è una stringa vuota e una falsità verificabile è uno stato di terminazione non riuscito di un comando, rappresentato da un valore diverso da zero.
Kaz,

8

Prima di affrontare il caso generale, possiamo discutere i tuoi contro esempi.

Confronti di stringhe

Lo stesso vale per molti tipi di confronti, in realtà. Tali confronti calcolano una distanza tra due oggetti. Quando gli oggetti sono uguali, la distanza è minima. Quindi, quando il "confronto ha esito positivo", il valore è 0. Ma in realtà, il valore di ritorno di nonstrcmp è un valore booleano, è una distanza e ciò che intrappola i programmatori inconsapevoli .if (strcmp(...)) do_when_equal() else do_when_not_equal()

In C ++ potremmo ridisegnare strcmpper restituire un Distanceoggetto, che sovrascrive operator bool()per restituire true quando 0 (ma verrai quindi morso da un diverso set di problemi). O in C semplice hanno solo una streqfunzione che restituisce 1 quando le stringhe sono uguali e 0 altrimenti.

Chiamate API / codice di uscita del programma

Qui ti preoccupi del motivo per cui qualcosa è andato storto, perché questo porterà a decisioni sbagliate. Quando le cose hanno successo, non vuoi sapere nulla in particolare: il tuo intento è realizzato. Il valore di ritorno deve quindi trasmettere queste informazioni. E ' non è un valore booleano, è un codice di errore. Il valore di errore speciale 0 significa "nessun errore". Il resto dell'intervallo rappresenta errori significativi localmente che devi affrontare (incluso 1, che spesso significa "errore non specificato").

Caso generale

Questo ci lascia con la domanda: perché i valori booleani Truee Falsecomunemente rappresentati con 1 e 0, rispettivamente?

Bene, oltre all'argomento soggettivo "si sente meglio così", ecco alcuni motivi (anche soggettivi) a cui posso pensare:

  • analogia del circuito elettrico. La corrente è ON per 1s e OFF per 0s. Mi piace avere (1, Sì, Vero, Acceso) insieme e (0, No, Falso, Spento), piuttosto che un altro mix

  • inizializzazioni di memoria. Quando ho memset(0)un sacco di variabili (siano esse ints, float, bool) voglio che il loro valore corrisponda alle ipotesi più conservative. Ad esempio, la mia somma è inizialmente 0, il predicato è False, ecc.

Forse tutte queste ragioni sono legate alla mia educazione - se mi avessero insegnato ad associare 0 a True sin dall'inizio, farei il contrario.


2
In realtà esiste almeno un linguaggio di programmazione che considera 0 come vero. La shell unix.
Jan Hudec,

+1 per affrontare il vero problema: la maggior parte della domanda di Morwenn non riguarda boolaffatto.
dan04,

@ dan04 Lo è. L'intero post riguarda la logica alla base della scelta del cast da inta boolin molti linguaggi di programmazione. Il paragone e la gestione degli errori sono solo esempi di luoghi in cui il cast di un altro modo rispetto a quello che ha attualmente fatto avrebbe senso.
Morwenn,

6

Da una prospettiva di alto livello, stai parlando di tre tipi di dati abbastanza diversi:

  1. Un booleano. La convenzione matematica nell'algebra booleana deve usare 0 per falsee 1 per true, quindi ha senso seguire quella convenzione. Penso che in questo modo abbia anche un senso più intuitivo.

  2. Il risultato del confronto. Questo ha tre valori: <, =e >(si noti che nessuno di loro è true). Per loro ha senso usare i valori di -1, 0 e 1, rispettivamente (o, più in generale, un valore negativo, zero e un valore positivo).

    Se vuoi verificare l'uguaglianza e hai solo una funzione che esegue un confronto generale, penso che dovresti renderlo esplicito usando qualcosa di simile strcmp(str1, str2) == 0. Trovo !confuso l' uso di questa situazione, perché tratta un valore non booleano come se fosse un valore booleano.

    Inoltre, tieni presente che il confronto e l'uguaglianza non devono essere la stessa cosa. Ad esempio, se ordini le persone entro la data di nascita, Compare(me, myTwin)dovrebbero tornare 0, ma Equals(me, myTwin)dovrebbero tornare false.

  3. Il successo o il fallimento di una funzione, possibilmente anche con dettagli su quel successo o fallimento. Se stai parlando di Windows, questo tipo viene chiamato HRESULTe un valore diverso da zero non indica necessariamente un errore. In effetti, un valore negativo indica fallimento e successo non negativo. Il valore di successo è molto spesso S_OK = 0, ma può anche essere ad esempio S_FALSE = 1o altri valori.

La confusione deriva dal fatto che tre tipi di dati logicamente abbastanza diversi sono effettivamente rappresentati come un singolo tipo di dati (un numero intero) in C e alcuni altri linguaggi e che è possibile utilizzare un numero intero in una condizione. Ma non penso che avrebbe senso ridefinire il booleano per rendere più semplice l'uso di alcuni tipi non booleani.

Inoltre, considera un altro tipo che viene spesso utilizzato in una condizione in C: un puntatore. Lì, è naturale trattare un NULLpuntatore (che è rappresentato come 0) come false. Quindi seguire il tuo suggerimento renderebbe anche più difficile lavorare con i puntatori. (Anche se, personalmente, preferisco confrontare esplicitamente i puntatori con NULL, anziché trattarli come booleani.)


4

Lo zero può essere falso perché la maggior parte delle CPU ha un flag ZERO che può essere usato per ramificare. Salva un'operazione di confronto.

Vediamo perché.

Alcuni psuedocode, poiché il pubblico probabilmente non legge l'assemblea

c-source richiami ad anello semplici per 10 volte

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

qualche finta assemblea per quello

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 un altro semplice loop chiama il wibble 10 volte

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

qualche finta assemblea per questo caso

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  

qualche altra fonte c

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

e l'assemblea

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

vedi quanto è corto?

qualche altra fonte c

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

e l'assembly (ipotizziamo un compilatore marginalmente intelligente che può sostituire == 0 senza confronto)

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

Ora proviamo una convenzione di true = 1

qualche altra fonte c #define TRUE 1 int foo = TRUE; if (foo == TRUE) wibble ()

e l'assemblea

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

vedi quanto è breve il caso con zero diverso da vero?

Davvero le prime CPU avevano piccoli set di bandiere attaccate all'accumulatore.

Per verificare se a> b o a = b richiede generalmente un'istruzione di confronto.

  • A meno che B sia ZERO - nel qual caso il flag ZERO è impostato Implementato come un semplice NOR logico o tutti i bit nell'accumulatore.
  • O NEGATIVO in cui basta usare il "bit di segno", ovvero il bit più significativo dell'accumulatore se si utilizza l'aritmetica del complemento a due. (Principalmente lo facciamo)

Riaffermiamo questo. Su alcune CPU meno recenti non è stato necessario utilizzare un'istruzione di confronto per l'accumulatore uguale a ZERO o l'accumulatore inferiore a zero.

Ora vedi perché zero potrebbe essere falso?

Si prega di notare che questo è psuedo-code e nessun set di istruzioni reale sembra abbastanza simile a questo. Se conosci il montaggio sai che sto semplificando molto le cose qui. Se sai qualcosa sulla progettazione del compilatore, non hai bisogno di leggere questa risposta. Chiunque sappia qualcosa sullo srotolamento del loop o sulla previsione del ramo, la classe avanzata è in fondo al corridoio nella stanza 203.


2
Il punto non è ben fatto qui perché per una cosa if (foo)e if (foo != 0)dovrebbe generare lo stesso codice, e in secondo luogo, si sta mostrando che il linguaggio assembly che si sta utilizzando, infatti, ha operandi booleani espliciti e prove per loro. Ad esempio jzsignifica jump if zero. In altre parole if (a == 0) goto target;. E la quantità non viene nemmeno testata direttamente; la condizione viene convertita in una bandiera booleana che viene memorizzata in una parola macchina speciale. In realtà è più simile acpu.flags.zero = (a == 0); if (cpu.flags.zero) goto target;
Kaz

No Kaz, le CPU più vecchie non funzionavano così. Il jz / jnz può essere eseguito senza eseguire un'istruzione di confronto. Questo è stato il punto del mio intero post davvero.
Tim Williscroft,

2
Non ho scritto nulla su un'istruzione di confronto.
Kaz,

Puoi citare un processore che ha jzun'istruzione ma no jnz? (o qualsiasi altra serie asimmetrica di istruzioni condizionali)
Toby Speight,

3

Ci sono molte risposte che suggeriscono che la corrispondenza tra 1 e vero è richiesta da alcune proprietà matematiche. Non riesco a trovare alcuna proprietà del genere e suggerisco che si tratti di una convenzione puramente storica.

Dato un campo con due elementi, abbiamo due operazioni: addizione e moltiplicazione. Possiamo mappare le operazioni booleane su questo campo in due modi:

Tradizionalmente, identifichiamo True con 1 e False con 0. Identifichiamo AND con * e XOR con +. Quindi OR sta saturando l'aggiunta.

Tuttavia, potremmo facilmente identificare True con 0 e False con 1. Quindi identifichiamo OR con * e XNOR con +. Quindi AND sta saturando l'aggiunta.


4
Se avessi seguito il link su Wikipedia potresti aver scoperto che il concetto di algebra booleana è chiuso in relazione a quello di un campo di Galois di due elementi ( en.wikipedia.org/wiki/GF%282%29 ). I simboli 0 e 1 sono convenzionalmente usate per contraddistinguere i additivi e moltiplicativi identità, rispettivamente, perché i numeri reali sono anche un campo le cui identità sono i numeri 0 e 1.
Giorgio

1
@NeilG Penso che Giorgio stia cercando di dire che è molto più di una semplice convention. 0 e 1 in algebra booleana sono sostanzialmente gli stessi di 0 e 1 in GF (2), che si comportano quasi come 0 e 1 in numeri reali per quanto riguarda l'addizione e la moltiplicazione.
svick,

1
@svick: No, perché puoi semplicemente rinominare la moltiplicazione e la saturazione dell'aggiunta in OR e AND e quindi capovolgere le etichette in modo che 0 sia True e 1 sia False. Giorgio sta dicendo che era una convenzione della logica booleana, che è stata adottata come una convenzione dell'informatica.
Neil G,

1
@Neil G: No, non puoi capovolgere + e * e 0 e 1 perché un campo richiede la distribuzione della moltiplicazione rispetto all'aggiunta (vedi en.wikipedia.org/wiki/Field_%28mathematics%29 ), ma se imposti +: = AND e *: = XOR, ottieni T XOR (T AND F) = T XOR F = T, mentre (T XOR T) AND (T XOR F) = F AND T = F. Pertanto, lanciando le operazioni e le identità che non hanno più un campo. Quindi l'IMO che definisce 0 e 1 come identità di un campo appropriato sembra catturare fedelmente il falso e il vero.
Giorgio,

1
@giorgio: ho modificato la risposta per rendere evidente cosa sta succedendo.
Neil G,

3

Stranamente, lo zero non è sempre falso.

In particolare, la convenzione Unix e Posix deve definire EXIT_SUCCESScome 0 (e EXIT_FAILUREcome 1). In realtà è persino una convenzione C standard !

Quindi per Posix shell e exit (2) syscalls, 0 significa "successo" che intuitivamente è più vero che falso.

In particolare, la shell ifvuole un ritorno al processo EXIT_SUCCESS(che è 0) per seguire il suo ramo "then"!

In Scheme (ma non in Common Lisp o in MELT ) 0 e zero (ovvero ()in Scheme) sono true, poiché l'unico valore falso è#f

Sono d'accordo, sto scherzando!


3

C viene utilizzato per la programmazione di basso livello vicino all'hardware, un'area in cui a volte è necessario passare da operazioni logiche a bit a bit, sugli stessi dati. Essere obbligati a convertire un'espressione numerica in booleano solo per eseguire un test ingombrerebbe il codice.

Puoi scrivere cose come:

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

piuttosto che

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

In un esempio isolato non è poi così male, ma dovendo farlo diventerà fastidioso.

Allo stesso modo, conversare operazioni. È utile che il risultato di un'operazione booleana, come un confronto, produca solo uno 0 o 1: Supponiamo di voler impostare il terzo bit di una parola in base al fatto che modemctrlil portatore rilevi il bit:

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

Qui dobbiamo avere != 0, per ridurre il risultato &dell'espressione biwise a 0o 1, ma poiché il risultato è solo un numero intero, ci viene risparmiato dal dover aggiungere un cast fastidioso per convertire ulteriormente il valore booleano in intero.

Anche se il moderno C ora ha un booltipo, conserva comunque la validità di codice come questo, sia perché è una buona cosa, sia a causa della massiccia rottura con la compatibilità all'indietro che sarebbe causata altrimenti.

Un altro esempio in cui C è fluido: testare due condizioni booleane come un interruttore a quattro vie:

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

Non puoi toglierlo dal programmatore C senza combattere!

Infine, C talvolta serve come una sorta di linguaggio assembly di alto livello. Nei linguaggi assembly, inoltre, non abbiamo tipi booleani. Un valore booleano è solo un bit o uno zero rispetto a un valore diverso da zero in una posizione di memoria o in un registro. Uno zero intero, zero booleano e l'indirizzo zero sono tutti testati allo stesso modo nei set di istruzioni del linguaggio assembly (e forse anche in virgola mobile zero). La somiglianza tra C e il linguaggio assembly è utile, ad esempio quando C viene utilizzato come lingua di destinazione per la compilazione di un'altra lingua (anche una che ha fortemente booleano tipizzato!)


0

Un valore booleano o di verità ha solo 2 valori. Vero e falso.

Questi non devono essere rappresentati come numeri interi, ma come bit (0 e 1).

Dire che qualsiasi altro numero intero accanto a 0 o 1 non è falso è un'affermazione confusa. Le tabelle di verità trattano valori di verità, non numeri interi.

Da un potenziale valore di verità, -1 o 2 spezzerebbero tutte le tabelle di verità e qualsiasi logica booleana associata a esse.

  • 0 E -1 ==?!
  • 0 OPPURE 2 ==?!

La maggior parte delle lingue di solito ha un booleantipo che quando viene castato su un tipo di numero come numero intero rivela false per essere lanciato come valore intero pari a 0.


1
0 E -1 == qualunque sia il valore booleano a cui li lanci. Questo è ciò che riguarda la mia domanda, perché lanciarli TRUEo FALSE. Non ho mai detto - forse l'ho fatto, ma non era previsto - i numeri interi erano veri o falsi, ho chiesto perché valutano a seconda di quando vengono lanciati su booleani.
Morwenn,

-6

In definitiva, stai parlando di rompere il linguaggio principale perché alcune API sono scadenti. Le API scadenti non sono nuove e non puoi risolverle interrompendo la lingua. È un fatto matematico che 0 è falso e 1 è vero, e qualsiasi linguaggio che non lo rispetti è fondamentalmente rotto. Il confronto a tre è di nicchia e non ha affari, il cui risultato viene convertito in modo implicito boolpoiché restituisce tre possibili risultati. Le vecchie API C hanno semplicemente una terribile gestione degli errori e sono anche bloccate perché C non ha le funzionalità linguistiche necessarie per non avere interfacce terribili.

Si noti che non lo sto dicendo per le lingue che non hanno una conversione booleana implicita intera.


11
"È un fatto matematico che 0 è falso e 1 è vero" Erm.
R. Martinho Fernandes,

11
Puoi citare un riferimento per il tuo "fatto matematico che 0 è falso e 1 è vero"? La tua risposta suona pericolosamente come uno sfogo.
Dan Pichelman,

14
Non è un fatto matematico, ma è stata una convenzione matematica dal 19 ° secolo.
Charles E. Grant,

1
L'algebra booleana è rappresentata da un campo finito in cui 0 e 1 sono gli elementi di identità per operazioni che assomigliano ad additon e moltiplicazione. Tali operazioni sono, rispettivamente, OR e AND. In effetti, l'algebra booleana è scritta in modo molto simile alla normale algebra in cui la giustapposizione indica AND, e il +simbolo indica OR. Quindi per esempio abc + a'b'csignifica (a and b and c) or (a and (not b) and (not c)).
Kaz,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.