Por que 0 é falso?


115

Essa pergunta pode parecer idiota, mas por que 0avalia falsee qualquer outro valor [inteiro] para a truemaioria das linguagens de programação?

Comparação de strings

Como a pergunta parece um pouco simples demais, vou me explicar um pouco mais: antes de tudo, pode parecer evidente para qualquer programador, mas por que não haveria uma linguagem de programação - pode realmente haver, mas não nenhuma Eu usei - para onde 0avalia truee todos os outros valores [inteiros] para false? Essa observação pode parecer aleatória, mas tenho alguns exemplos em que pode ter sido uma boa ideia. Primeiro de tudo, vamos dar o exemplo de comparação tríplice de strings, vou usar C strcmpcomo exemplo: qualquer programador que tente C como sua primeira língua pode ser tentado a escrever o seguinte código:

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

Como strcmpretornos 0que avaliam falsequando as strings são iguais, o que o programador iniciante tentou fazer falha miseravelmente e ele geralmente não entende o porquê. Em vez disso, se tivesse 0avaliado true, essa função poderia ter sido usada em sua expressão mais simples - a acima - ao comparar a igualdade, e as verificações apropriadas -1e 1teriam sido realizadas somente quando necessário. Teríamos considerado o tipo de retorno como bool(na minha opinião, quero dizer) na maioria das vezes.

Além disso, vamos apresentar um novo tipo sign,, que apenas aceita valores -1, 0e 1. Isso pode ser bastante útil. Imagine que existe um operador de espaçonave em C ++ e queremos isso std::string(bem, já existe a comparefunção, mas o operador de espaçonave é mais divertido). A declaração seria atualmente a seguinte:

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

Se tivesse 0sido avaliado true, o operador da nave espacial nem existiria, e poderíamos ter declarado operator==assim:

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

Isso operator==teria tratado a comparação de três maneiras ao mesmo tempo e ainda poderia ser usado para executar a verificação a seguir, enquanto ainda é possível verificar qual sequência é lexicograficamente superior à outra quando necessário:

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

Tratamento de erros antigos

Agora temos exceções, portanto essa parte se aplica apenas aos idiomas antigos onde não existe (C por exemplo). Se olharmos para a biblioteca padrão de C (e também para a POSIX), podemos ver com certeza que muitas funções retornam 0quando bem-sucedidas e qualquer número inteiro caso contrário. Infelizmente, tenho visto algumas pessoas fazerem esse tipo de coisa:

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

Se pensarmos em como pensamos em programação, geralmente temos o seguinte padrão de raciocínio:

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

Se pensarmos sobre isso de novo, teria sentido colocar o único valor neutro,, 0para yes(e é assim que as funções de C funcionam), enquanto todos os outros valores podem estar lá para resolver os muitos casos do no. No entanto, em todas as linguagens de programação que conheço (exceto talvez algumas linguagens esotéricas experimentais), isso é yesavaliado falseem uma ifcondição, enquanto todos os nocasos são avaliados em true. Existem muitas situações em que "funciona" representa um caso, enquanto "não funciona" representa muitas causas prováveis. Se pensarmos dessa maneira, ter 0avaliado truee o resto falseteria feito muito mais sentido.

Conclusão

Minha conclusão é essencialmente minha pergunta original: por que projetamos linguagens onde 0estão falsee outros valores true, levando em conta meus poucos exemplos acima e talvez mais alguns em que eu não pensava?

Acompanhamento: É bom ver que existem muitas respostas com muitas idéias e o maior número possível de razões para que seja assim. Eu amo como você parece apaixonado por isso. Eu originalmente fiz essa pergunta por tédio, mas como você parece tão apaixonado, decidi ir um pouco mais longe e perguntar sobre a lógica por trás da escolha booleana de 0 e 1 em Math.SE :)


32
strcmp()não é um bom exemplo para true ou false, pois retorna 3 valores diferentes. E você ficará surpreso quando começar a usar um shell, onde 0 significa verdadeiro e qualquer outra coisa significa falso.
15--13

52
@ ott--: Nos shells do Unix, 0 significa sucesso e diferente de zero significa falha - não é exatamente a mesma coisa que "verdadeiro" e "falso".
Keith Thompson

14
@ KeithThompson: No Bash (e em outras conchas), "sucesso" e "fracasso" são realmente o mesmo que "verdadeiro" e "falso". Considere, por exemplo, a declaração if true ; then ... ; fi, onde trueestá um comando que retorna zero e indica ifpara executar ....
Ruakh

13
Não há booleanos no hardware, apenas números binários e, na maioria dos ISAs históricos, um número diferente de zero é considerado "verdadeiro" em todas as instruções de ramificação condicional (a menos que estejam usando sinalizadores). Portanto, as linguagens de baixo nível são obrigadas por todos os meios a seguir as propriedades de hardware subjacentes.
SK-logic

2
@MasonWheeler Ter um tipo booleano não implica nada. Por exemplo python faz ter um booltipo, mas comparações / se as condições etc. pode ter qualquer valor de retorno.
Bakuriu 16/05

Respostas:


98

0é falseporque ambos são zero elementos em semirriscos comuns . Mesmo sendo tipos de dados distintos, faz sentido intuitivo converter entre eles porque pertencem a estruturas algébricas isomórficas.

  • 0é a identidade para adição e zero para multiplicação. Isso vale para números inteiros e racionais, mas não para números de ponto flutuante IEEE-754: 0.0 * NaN = NaNe 0.0 * Infinity = NaN.

  • falseé a identidade para booleano xor (⊻) e zero para booleano e (∧). Se os booleanos são representados como {0, 1} - o conjunto de números inteiros módulo 2 - você pode pensar em ⊻ como adição sem transporte e ∧ como multiplicação.

  • ""e []são identidades para concatenação, mas existem várias operações para as quais elas fazem sentido como zero. A repetição é uma, mas a repetição e a concatenação não se distribuem; portanto, essas operações não formam um semicondutor.

Tais conversões implícitas são úteis em pequenos programas, mas, em geral, podem dificultar a discussão de programas. Apenas uma das muitas desvantagens no design de idiomas.


1
Bom que você mencionou listas. (BTW, nilexiste a lista vazia []e o falsevalor no Common Lisp; existe uma tendência de mesclar identidades de diferentes tipos de dados?) Você ainda precisa explicar por que é natural considerar falso como uma identidade aditiva e verdadeiro como uma identidade multiplicativa e não o contrário. Não é possível considerar truecomo identificar ANDe zero para OR?
Giorgio

3
+1 por se referir a identidades semelhantes. Finalmente, uma resposta que não se resume apenas a "convenção, lide com ela".
L0b0

5
+1 para dar detalhes de uma concreta e matemática muito velhos em que esta tem sido seguido e longo fez sentido
Jimmy Hoffa

1
Esta resposta não faz sentido. trueé também a identidade e o zero de semirriscos (booleano e / ou). Segundo a convenção, não há razão para considerar que falseseja mais próximo de 0 que true.
TonioElGringo

1
@TonioElGringo: A diferença entre verdadeiro e falso é a diferença entre XOR e XNOR. Pode-se formar anéis isomórficos usando AND / XOR, onde verdadeiro é a identidade multiplicativa e falso o aditivo, ou com OR e XNOR, onde falso é a identidade multiplicativa e verdadeiro é o aditivo, mas o XNOR geralmente não é considerado um elemento comum. operação fundamental do jeito que o XOR é.
Supercat 9/17

74

Porque a matemática funciona.

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

... insert many other examples here.

Tradicionalmente, os programas C têm condições como

if (someFunctionReturningANumber())

ao invés de

if (someFunctionReturningANumber() != 0)

porque o conceito de zero sendo equivalente a falso é bem compreendido.


21
As linguagens são projetadas assim porque a matemática faz sentido. Isso veio primeiro.
Robert Harvey

26
@ Morwenn, remonta ao século 19 e George Boole. As pessoas representam Falso como 0 e Verdadeiro como! 0 por mais tempo do que os computadores.
Charles E. Grant

11
Não vejo por que a matemática não funciona da outra maneira se você apenas altera todas as definições para que AND seja + e OR seja *.
Neil G

7
Exatamente: a matemática funciona nos dois sentidos e a resposta a essa pergunta parece ser que é puramente convencional.
Neil G

6
@ Robert Seria ótimo se você pudesse explicar os "fundamentos matemáticos" em seu post.
Phant0m

38

Como outros já disseram, a matemática veio primeiro. É por isso que 0 é falsee 1 é true.

De que matemática estamos falando? Álgebras booleanas que datam de meados do século XIX, muito antes do surgimento dos computadores digitais.

Você também pode dizer que a convenção surgiu da lógica proposicional , que é ainda mais antiga que as álgebras booleanas. Essa é a formalização de muitos dos resultados lógicos que os programadores conhecem e amam ( false || xiguais x, true && xiguais xe assim por diante).

Basicamente, estamos falando de aritmética em um conjunto com dois elementos. Pense em contar em binário. As álgebras booleanas são a origem desse conceito e sua base teórica. As convenções de idiomas como C são apenas uma aplicação direta.


2
Você poderia, com certeza. Mas mantê-lo da maneira "padrão" se encaixa bem na aritmética geral (0 + 1 = 1, não 0 + 1 = 0).
Joshin4colours #

2
Sim, mas você provavelmente escreveria AND com + e OR com * se também revertesse as definições.
Neil G

3
A matemática não veio primeiro. A matemática reconheceu que 0 e 1 formam um campo, no qual AND é como multiplicação e OR é como adição.
Kaz

1
@Kaz: Mas {0, 1} com OR e AND não forma um campo.
Giorgio

2
Incomoda-me um pouco que mais respostas e comentários digam isso true = 1. Isso não é exatamente exato, porque o true != 0que não é exatamente o mesmo. Uma razão (e não a única) pela qual se deve evitar comparações if(something == true) { ... }.
JensG

26

Eu pensei que isso tinha a ver com a "herança" da eletrônica e também da álgebra booleana, onde

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

strcmp retorna 0 quando as strings são iguais tem a ver com sua implementação, pois o que realmente faz é calcular a "distância" entre as duas strings. Que 0 também seja considerado falso é apenas uma coincidência.

retornar 0 com êxito faz sentido, porque 0, nesse caso, é usado para significar nenhum erro e qualquer outro número seria um código de erro. Usar qualquer outro número para obter sucesso faria menos sentido, pois você possui apenas um código de sucesso, enquanto você pode ter vários códigos de erro. Você usa "Funcionou?" como a expressão if e diga 0 = yes, faria mais sentido, mas a expressão é mais correta "Alguma coisa deu errado?" e então você vê que 0 = não faz muito sentido. Pensar false/truerealmente não faz sentido aqui, como é realmente no error code/error code.


Haha, você é o primeiro a declarar explicitamente a questão do erro de retorno. Eu já sabia que o interpretei do meu jeito e que poderia ser perguntado de outra maneira, mas você é o primeiro a expressá-lo explicitamente (das muitas respostas e comentários). Na verdade, eu não diria que um ou outro modo não faz sentido, mas mais que tanto fazem sentido em maneiras diferentes :)
Morwenn

1
Na verdade, eu diria que 0para success/no erroré a única coisa que faz sentido quando outras inteiros representam os códigos de erro. Isso 0também acontece para representar falseem outros casos realmente não importa, já que não estamos a falar de verdadeiro ou falso aqui em tudo;)
Svish

Eu tive a mesma idéia, então eu levantei
user60812

1
Seu ponto de vista sobre o strcmp()cálculo da distância é bastante bom. Se tivesse sido chamado strdiff(), if (!strdiff())seria muito lógico.
Kevin Cox

"eletrônicos [...] onde 0 = [...] falso, 1 = [...] verdadeiro" - mesmo em eletrônicos, isso é apenas uma convenção e não é o único. Chamamos isso de lógica positiva, mas você também pode usar lógica negativa, onde uma tensão positiva indica falsa e negativa indica verdadeira. Então, o circuito que você usaria para AND se torna OR, OR se torna AND e assim por diante. Devido à lei de De Morgan, tudo acaba sendo equivalente. Às vezes, você encontrará parte de um circuito eletrônico implementado em lógica negativa por conveniência, quando os nomes dos sinais nessa parte são anotados com uma barra acima deles.
Jules

18

Conforme explicado neste artigo , os valores falsee truenão devem ser confundidos com os inteiros 0 e 1, mas podem ser identificados com os elementos do campo Galois (campo finito) de dois elementos (veja aqui ).

Um campo é um conjunto com duas operações que satisfazem certos axiomas.

Os símbolos 0 e 1 são convencionalmente usados ​​para denotar as identidades aditiva e multiplicativa de um campo porque os números reais também são um campo (mas não finito) cujas identidades são os números 0 e 1.

A identidade aditiva é o elemento 0 do campo, de modo que para todos os x:

x + 0 = 0 + x = x

e a identidade multiplicativa é o elemento 1 do campo, de modo que para todos os x:

x * 1 = 1 * x = x

O campo finito de dois elementos possui apenas esses dois elementos, a identidade aditiva 0 (ou false) e a identidade multiplicativa 1 (ou true). As duas operações desse campo são o XOR lógico (+) e o AND lógico (*).

Nota. Se você inverte as operações (XOR é a multiplicação e AND é a adição), a multiplicação não é distributiva sobre a adição e você não tem mais um campo. Nesse caso, você não tem motivos para chamar os dois elementos 0 e 1 (em qualquer ordem). Observe também que você não pode escolher a operação OR em vez de XOR: não importa como você interpreta OR / AND como adição / multiplicação, a estrutura resultante não é um campo (nem todos os elementos inversos existem conforme exigido pelos axiomas do campo).

Em relação às funções C:

  • Muitas funções retornam um número inteiro que é um código de erro. 0 significa SEM ERRO.
  • Intuitivamente, a função strcmpcalcula a diferença entre duas strings. 0 significa que não há diferença entre duas strings, ou seja, que duas strings são iguais.

As explicações intuitivas acima podem ajudar a lembrar a interpretação dos valores retornados, mas é ainda mais fácil verificar a documentação da biblioteca.


1
+1 por mostrar que, se você os trocar arbitrariamente, a matemática não funcionará mais.
Jimmy Hoffa

2
Invertida: dado um campo com dois elementos e operações * e +, identificamos True com 0 e False com 1. Identificamos OR com * e XOR com +.
Neil G

1
Você descobrirá que essas duas identificações são feitas no mesmo campo e são consistentes com as regras da lógica booleana. Sua nota é infelizmente :) incorreta
Neil G

1
Se você assumir que True = 0 e XOR é +, True deve ser a identidade para XOR. Mas não é porque True XOR True = False. A menos que você redefina a operação XOR em True para que True XOR True = True. Então, é claro, sua construção funciona porque você acabou de renomear as coisas (em qualquer estrutura matemática, você sempre pode fazer uma permutação de nome e obter uma estrutura isomórfica). Por outro lado, se você deixá-Verdadeiro, Falso e XOR têm o seu significado usual, então verdadeiro XOR True = False e True não pode ser a identidade aditiva, ou seja verdadeiro não pode ser 0.
Giorgio

1
@Giorgio: Corrigi minha construção de acordo com o seu comentário no meu último comentário ...
Neil G

13

Você deve considerar que sistemas alternativos também podem ser decisões de design aceitáveis.

Blocos: 0 status de saída é verdadeiro, diferente de zero é falso

O exemplo de shells tratando um status de saída 0 como true já foi mencionado.

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

A lógica é que existe uma maneira de obter sucesso, mas há muitas maneiras de falhar; portanto, usar 0 como o valor especial que significa "sem erros" é pragmático.

Ruby: 0 é como qualquer outro número

Entre as linguagens de programação "normais", existem alguns outliers, como Ruby, que tratam 0 como um valor verdadeiro.

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

A lógica é que apenas falsee nildeve ser falsa. Para muitos novatos em Ruby, é uma pegadinha. No entanto, em alguns casos, é bom que 0 seja tratado como qualquer outro número.

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"

No entanto, esse sistema funciona apenas em um idioma capaz de distinguir booleanos como um tipo separado de números. Nos primeiros dias da computação, os programadores que trabalhavam com linguagem assembly ou linguagem de máquina bruta não tinham tais luxos. Provavelmente, é natural tratar 0 como o estado "em branco" e definir um pouco como 1 como sinalizador quando o código detectar que algo aconteceu. Por extensão, a convenção desenvolveu que zero era tratado como falso e valores diferentes de zero passaram a ser tratados como verdadeiros. No entanto, não precisa ser assim.

Java: Os números não podem ser tratados como booleanos

Em Java, truee falsesão os únicos valores booleanos. Os números não são booleanos e nem podem ser convertidos em booleanos ( Java Language Specification, Seção 4.2.2 ):

Não há conversão entre tipos integrais e o tipo boolean.

Essa regra evita a questão completamente - todas as expressões booleanas precisam ser explicitamente escritas no código.


1
Rebol e Red tratam INTEGER com valor 0! valores como true e tenha um NONE separado! tipo (com apenas um valor, NENHUM) tratado como falso condicional, além de LOGIC! falso. Eu encontrei uma frustração significativa ao tentar escrever código JavaScript que trata 0 como falso; é uma decisão incrivelmente desajeitada para uma linguagem de tipo dinâmico. Se você deseja testar algo que pode ser nulo ou 0, você acaba tendo que escrever if (thing === 0), isso não é legal.
HostileFork

@HostileFork eu não sei. Acho que faz sentido que (como qualquer outro número inteiro) 0esteja trueem uma linguagem dinâmica. Às vezes, eu pegava um 0ao tentar pegar Noneno Python, e isso às vezes pode ser bem difícil de detectar.
precisa saber é o seguinte

2
Ruby não é um outlier. Ruby tira isso do Lisp (o Ruby é chamado secretamente de "MatzLisp"). Lisp é uma linguagem comum em ciência da computação. Zero também é apenas um verdadeiro valor no shell POSIX, porque é um pedaço de texto: if [ 0 ] ; then echo this executes ; fi. O valor de dados falsos é uma cadeia vazia e uma falsidade testável é um status de finalização com falha de um comando, representado por um valor diferente de zero.
Kaz

8

Antes de abordar o caso geral, podemos discutir seus exemplos contrários.

Comparações de strings

O mesmo vale para muitos tipos de comparações, na verdade. Tais comparações calculam uma distância entre dois objetos. Quando os objetos são iguais, a distância é mínima. Portanto, quando a "comparação é bem-sucedida", o valor é 0. Mas, na verdade, o valor de retorno de nãostrcmp é um booleano, é uma distância, e é isso que as armadilhas ignoram os programadores .if (strcmp(...)) do_when_equal() else do_when_not_equal()

Em C ++, podemos redesenhar strcmppara retornar um Distanceobjeto, que substitui operator bool()para retornar true quando 0 (mas você seria mordido por um conjunto diferente de problemas). Ou, na linguagem C, basta ter uma streqfunção que retorne 1 quando as strings forem iguais e 0, caso contrário.

Chamadas API / código de saída do programa

Aqui você se preocupa com o motivo pelo qual algo deu errado, porque isso levará as decisões a erro. Quando as coisas dão certo, você não quer saber nada em particular - sua intenção é realizada. O valor de retorno deve, portanto, transmitir essas informações. É não um booleano, é um código de erro. O valor do erro especial 0 significa "sem erro". O restante do intervalo representa erros significativos localmente com os quais você precisa lidar (incluindo 1, que geralmente significa "erro não especificado").

Caso Geral

Isso nos deixa com a pergunta: por que os valores booleanos Truee Falsecomumente representados com 1 e 0, respectivamente?

Bem, além do argumento subjetivo "parece melhor assim", aqui estão algumas razões (subjetivas também) em que posso pensar:

  • analogia do circuito elétrico. A corrente está LIGADA por 1s e DESLIGADA por 0s. Eu gosto de ter (1, Sim, Verdadeiro, Ativado) juntos e (0, Não, Falso, Desativado), em vez de outro mix

  • inicializações de memória. Quando memset(0)um monte de variáveis ​​(sejam elas ints, floats, bools), quero que seu valor corresponda às suposições mais conservadoras. Por exemplo, minha soma é inicialmente 0, o predicado é Falso etc.

Talvez todas essas razões estejam ligadas à minha educação - se eu tivesse sido ensinado a associar 0 a True desde o início, eu faria o contrário.


2
Na verdade, há pelo menos uma linguagem de programação que trata 0 como verdadeira. O shell unix.
Jan Hudec

+1 para abordar o problema real: a maioria das perguntas de Morwenn não tem boola ver.
Dan04

@ dan04 É. Todo o post é sobre a lógica por trás da escolha do elenco de intpara boolem muitas linguagens de programação. As coisas de comparação e gestos de erro são apenas exemplos de lugares em que a transmissão de outra maneira, além da que é feita atualmente, faria sentido.
Morwenn

6

De uma perspectiva de alto nível, você está falando sobre três tipos de dados bastante diferentes:

  1. Um booleano. A convenção matemática na álgebra booleana é usar 0 para falsee 1 para true, portanto, faz sentido seguir essa convenção. Eu acho que dessa maneira também faz mais sentido intuitivamente.

  2. O resultado da comparação. Isto tem três valores: <, =e >(aviso de que nenhum deles é true). Para eles, faz sentido usar os valores de -1, 0 e 1, respectivamente (ou, geralmente, um valor negativo, zero e um valor positivo).

    Se você deseja verificar a igualdade e você só tem uma função que realiza comparação geral, acho que você deve explicitar isso usando algo como strcmp(str1, str2) == 0. Acho que o uso !nessa situação é confuso, porque trata um valor não-booleano como se fosse um booleano.

    Além disso, lembre-se de que comparação e igualdade não precisam ser a mesma coisa. Por exemplo, se você solicitar as pessoas pela data de nascimento, elas Compare(me, myTwin)devem retornar 0, mas Equals(me, myTwin)devem retornar false.

  3. O sucesso ou falha de uma função, possivelmente também com detalhes sobre esse sucesso ou falha. Se você está falando do Windows, esse tipo é chamado HRESULTe um valor diferente de zero não indica necessariamente falha. De fato, um valor negativo indica falha e sucesso não negativo. O valor do sucesso é muito frequente S_OK = 0, mas também pode ser, por exemplo S_FALSE = 1, ou outros valores.

A confusão vem do fato de que três tipos de dados logicamente bastante diferentes são realmente representados como um único tipo de dados (um número inteiro) em C e em alguns outros idiomas e que você pode usar números inteiros em uma condição. Mas acho que não faria sentido redefinir booleano para simplificar o uso de alguns tipos não-booleanos.

Além disso, considere outro tipo que é frequentemente usado em uma condição em C: um ponteiro. Lá, é natural tratar um NULLponteiro (que é representado como 0) como false. Portanto, seguir sua sugestão também tornaria o trabalho com ponteiros mais difícil. (Embora, pessoalmente, prefiro comparar explicitamente os ponteiros com NULL, em vez de tratá-los como booleanos.)


4

Zero pode ser falso, porque a maioria das CPUs possui um sinalizador ZERO que pode ser usado para ramificar. Ele salva uma operação de comparação.

Vamos ver o porquê.

Algum código psued, como o público provavelmente não lê assembly

o loop simples de origem c chama wibble 10 vezes

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

alguns fingem montagem para isso

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 - fonte outro loop simples chama wibble 10 vezes

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

alguns fingem montagem para este 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  

um pouco mais de fonte c

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

e a montagem

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

vê como é curto?

um pouco mais de fonte c

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

e o assembly (vamos assumir um compilador marginalmente inteligente que pode substituir == 0 sem comparação)

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

Agora vamos tentar uma convenção de true = 1

um pouco mais de fonte c #define TRUE 1 int foo = TRUE; if (foo == TRUE) wibble ()

e a montagem

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

ver quão curto é o caso com diferente de zero verdadeiro?

As CPUs muito antigas tinham pequenos conjuntos de sinalizadores anexados ao Acumulador.

Para verificar se a> b ou a = b geralmente usa uma instrução de comparação.

  • A menos que B seja ZERO - nesse caso, o sinalizador ZERO é definido como Implementado como uma NOR lógica simples ou todos os bits no Acumulador.
  • Ou NEGATIVO, no qual basta usar o "bit de sinal", ou seja, o bit mais significativo do acumulador se você estiver usando a aritmética de complemento de dois. (Principalmente nós fazemos)

Vamos reafirmar isso. Em algumas CPUs mais antigas, você não precisava usar uma instrução de comparação para acumulador igual a ZERO ou acumulador menor que zero.

Agora você vê por que zero pode ser falso?

Observe que este é o código psuedo e nenhum conjunto de instruções real se parece com isso. Se você conhece montagem, sabe que estou simplificando bastante as coisas aqui. Se você conhece alguma coisa sobre o design do compilador, não precisa ler esta resposta. Qualquer pessoa que saiba algo sobre desenrolamento de loop ou previsão de ramificação, a classe avançada fica no final do corredor na sala 203.


2
Seu ponto não é bem feito aqui, porque para uma coisa if (foo)e if (foo != 0)deve gerar o mesmo código, e em segundo lugar, você está mostrando que a linguagem assembly que você está usando na verdade tem operandos booleanos explícitas e testes para eles. Por exemplo, jzsignifica jump if zero. Em outras palavras if (a == 0) goto target;. E a quantidade nem está sendo testada diretamente; a condição é convertida em um sinalizador booleano que é armazenado em uma palavra de máquina especial. Na verdade, é mais parecido comcpu.flags.zero = (a == 0); if (cpu.flags.zero) goto target;
Kaz

Sem Kaz, as CPUs mais antigas não funcionavam assim. O jz / jnz pode ser executado sem fazer uma instrução de comparação. Qual foi o sentido de todo o meu post realmente.
Tim Williscroft

2
Não escrevi nada sobre uma instrução de comparação.
Kaz

Você pode citar um processador que possui uma jzinstrução, mas não jnz? (ou qualquer outro conjunto de instruções assimétrica condicionais)
Toby Speight

3

Há muitas respostas que sugerem que a correspondência entre 1 e verdadeira é necessária por alguma propriedade matemática. Não encontro nenhuma dessas propriedades e sugiro que seja uma convenção puramente histórica.

Dado um campo com dois elementos, temos duas operações: adição e multiplicação. Podemos mapear operações booleanas nesse campo de duas maneiras:

Tradicionalmente, identificamos True com 1 e False com 0. Identificamos AND com * e XOR com +. Assim OU é adição saturante.

No entanto, podemos facilmente identificar True com 0 e False com 1. Em seguida, identificamos OR com * e XNOR com +. Assim, AND é adição saturante.


4
Se você seguisse o link na wikipedia, poderia descobrir que o conceito de álgebra booleana está fechado, relacionado ao de um campo de Galois de dois elementos ( en.wikipedia.org/wiki/GF%282%29 ). Os símbolos 0 e 1 são usadas convencionalmente para designar os aditivos e multiplicativos identidades, respectivamente, porque os números reais são também um campo cujas identidades são os números 0 e 1.
Giorgio

1
@ NeilG Acho que Giorgio está tentando dizer que é mais do que apenas uma convenção. 0 e 1 na álgebra booleana são basicamente os mesmos que 0 e 1 em GF (2), que se comportam quase o mesmo que 0 e 1 em números reais no que diz respeito à adição e multiplicação.
Svick

1
@svick: Não, porque você pode simplesmente renomear a multiplicação e a adição de saturação como OR e AND e depois virar os rótulos para que 0 seja Verdadeiro e 1 seja Falso. Giorgio está dizendo que era uma convenção da lógica booleana, que foi adotada como uma convenção da ciência da computação.
Neil G

1
@ Neil G: Não, você não pode ativar + e * e 0 e 1 porque um campo requer distributividade da multiplicação sobre adição (consulte en.wikipedia.org/wiki/Field_%28mathematics%29 ), mas se você definir +: = AND e *: = XOR, você obtém T XOR (T e F) = T XOR F = T, enquanto que (T XOR T) AND (T XOR F) = F AND T = F. Portanto, invertendo as operações e as identidades que você não tem mais um campo. Portanto, a IMO que define 0 e 1 como as identidades de um campo apropriado parece capturar o falso e o verdadeiro com bastante fidelidade.
Giorgio

1
@ Giorgio: Eu editei a resposta para tornar óbvio o que está acontecendo.
Neil G

3

Estranhamente, zero nem sempre é falso.

Em particular, a convenção Unix e Posix é definir EXIT_SUCCESScomo 0 (e EXIT_FAILUREcomo 1). Na verdade, é mesmo uma convenção C padrão !

Portanto, para shells Posix e syscalls exit (2) , 0 significa "bem-sucedido", que intuitivamente é mais verdadeiro que falso.

Em particular, o shell ifdeseja que um retorno do processo EXIT_SUCCESS(que seja 0) siga sua ramificação "then"!

No Esquema (mas não no Common Lisp ou no MELT ) 0 e nulo (ou seja, ()no Esquema) são verdadeiros, pois o único valor falso é#f

Eu concordo, eu estou nitpicking!


3

C é usado para programação de baixo nível perto do hardware, uma área na qual você às vezes precisa alternar entre operações lógicas e bit a bit, nos mesmos dados. Ser obrigado a converter uma expressão numérica em booleano apenas para executar um teste desorganizaria o código.

Você pode escrever coisas como:

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

ao invés de

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

Em um exemplo isolado, não é tão ruim, mas ter que fazer isso será cansativo.

Da mesma forma, converse operações. É útil para o resultado de uma operação booleana, como uma comparação, produzir apenas 0 ou 1: suponha que desejamos definir o terceiro bit de alguma palavra com base no fato de modemctrla operadora detectar o bit:

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

Aqui temos que ter != 0, para reduzir o resultado da &expressão biwise para 0ou 1, mas como o resultado é apenas um número inteiro, não somos obrigados a adicionar uma conversão irritante para converter booleano em número inteiro.

Embora o C moderno agora tenha um booltipo, ele ainda preserva a validade do código como este, tanto por ser uma coisa boa quanto por causa da quebra maciça de compatibilidade com versões anteriores que seria causada de outra forma.

Outro exemplo em que C é liso: testando duas condições booleanas como uma chave de quatro direções:

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

Você não poderia tirar isso do programador C sem lutar!

Por fim, C às vezes serve como um tipo de linguagem assembly de alto nível. Nas linguagens assembly, também não temos tipos booleanos. Um valor booleano é apenas um bit ou zero versus um valor diferente de zero em um local ou registro de memória. Um zero inteiro, zero booleano e o endereço zero são todos testados da mesma maneira nos conjuntos de instruções em linguagem assembly (e talvez até no ponto flutuante zero). A semelhança entre C e a linguagem assembly é útil, por exemplo, quando C é usado como o idioma de destino para compilar outro idioma (mesmo um que tenha fortemente digitado booleanos!)


0

Um valor booleano ou verdade possui apenas 2 valores. Verdadeiro e falso.

Estes não devem ser representados como números inteiros, mas como bits (0 e 1).

Dizer que qualquer outro número inteiro ao lado de 0 ou 1 não é falso é uma afirmação confusa. As tabelas de verdade lidam com valores de verdade, não com números inteiros.

De um valor de verdade prospectivo, -1 ou 2 quebrariam todas as tabelas de verdade e qualquer lógica booleana associada a elas.

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

A maioria dos idiomas geralmente possui um booleantipo que, quando convertido para um tipo de número, como número inteiro, revela que falso é convertido como um valor inteiro 0.


1
0 AND -1 == qualquer valor booleano em que você os converter. É sobre isso que minha pergunta é: por que lançá-los para TRUEou FALSE. Nunca disse - talvez sim, mas não foi intencional - números inteiros eram verdadeiros ou falsos, perguntei por que eles avaliam o que quer que seja quando convertidos em booleanos.
Morwenn 15/05

-6

Por fim, você está falando em quebrar a linguagem principal porque algumas APIs são ruins. APIs ruins não são novas e você não pode corrigi-las quebrando o idioma. É um fato matemático que 0 é falso e 1 é verdadeiro, e qualquer linguagem que não respeite isso é fundamentalmente interrompida. A comparação de três vias é um nicho e não tem nenhum negócio, tendo o resultado convertido implicitamente em, boolpois retorna três resultados possíveis. As APIs antigas do C simplesmente têm um tratamento de erros terrível e também são prejudicadas porque o C não possui os recursos de linguagem necessários para não ter interfaces terríveis.

Observe que não estou dizendo isso para idiomas que não possuem conversão implícita de número inteiro-> booleano.


11
"É um fato matemático que 0 é falso e 1 é verdadeiro" Erm.
R. Martinho Fernandes

11
Você pode citar uma referência para o seu "fato matemático de que 0 é falso e 1 é verdadeiro"? Sua resposta soa perigosamente como um discurso retórico.
Dan Pichelman

14
Não é um fato matemático, mas tem sido uma convenção matemática desde o século XIX.
Charles E. Grant

1
A álgebra booleana é representada por um campo finito no qual 0 e 1 são os elementos de identidade para operações que se assemelham a adição e multiplicação. Essas operações são, respectivamente, OR e AND. De fato, a álgebra booleana é escrita como álgebra normal, onde justaposição denota AND e o +símbolo denota OR. Então, por exemplo, abc + a'b'csignifica (a and b and c) or (a and (not b) and (not c)).
Kaz
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.