¿Por qué es 0 falso?

115

Esta pregunta puede sonar tonta, pero ¿por qué 0evalúa falsey cualquier otro valor [entero] truees la mayoría de los lenguajes de programación?

Comparación de cadenas

Dado que la pregunta parece demasiado simple, me explicaré un poco más: en primer lugar, puede parecer evidente para cualquier programador, pero ¿por qué no habría un lenguaje de programación? Puede haberlo, pero no ninguno Solía: ¿dónde se 0evalúa truey todos los demás valores [enteros] false? Ese comentario puede parecer aleatorio, pero tengo algunos ejemplos en los que puede haber sido una buena idea. En primer lugar, tomemos el ejemplo de la comparación de tres vías de cadenas, tomaré C strcmpcomo ejemplo: cualquier programador que intente C como su primer lenguaje puede verse tentado a escribir el siguiente código:

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

Como los strcmpretornos 0que se evalúan falsecuando las cadenas son iguales, lo que el programador principiante intentó hacer falla miserablemente y generalmente no comprende por qué al principio. En cambio, si hubiera 0evaluado trueesta función, podría haberse utilizado en su expresión más simple, la anterior, al comparar la igualdad, y las comprobaciones adecuadas -1y 1se habrían realizado solo cuando fuera necesario. Hubiéramos considerado el tipo de retorno como bool(en nuestra opinión, quiero decir) la mayor parte del tiempo.

Además, vamos a introducir un nuevo tipo sign, que solo toma valores -1, 0y 1. Eso puede ser bastante útil. Imagine que hay un operador de nave espacial en C ++ y lo queremos std::string(bueno, ya existe la comparefunción, pero el operador de nave espacial es más divertido). La declaración actualmente sería la siguiente:

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

Si hubiera 0sido evaluado true, el operador de la nave espacial ni siquiera existiría, y podríamos haber declarado de operator==esa manera:

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

Esto operator==habría manejado la comparación de tres vías a la vez, y aún podría usarse para realizar la siguiente verificación y al mismo tiempo poder verificar qué cadena es lexicográficamente superior a la otra cuando sea necesario:

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

Manejo de errores antiguos

Ahora tenemos excepciones, por lo que esta parte solo se aplica a los lenguajes antiguos donde no existe tal cosa (C por ejemplo). Si miramos la biblioteca estándar de C (y POSIX uno también), podemos ver con certeza que las funciones pueden regresar 0cuando tienen éxito y cualquier número entero de lo contrario. Lamentablemente he visto a algunas personas hacer este tipo de cosas:

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

Si pensamos en cómo pensamos en la programación, a menudo tenemos el siguiente patrón de razonamiento:

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

Si lo pensamos nuevamente, tendría sentido poner el único valor neutral 0, a yes(y así es como funcionan las funciones de C), mientras que todos los demás valores pueden estar allí para resolver los muchos casos de no. Sin embargo, en todos los lenguajes de programación que conozco (excepto quizás algunos lenguajes esotéricos experimentales), que yesevalúa falseen una ifcondición, mientras que todos los nocasos evalúan true. Hay muchas situaciones en las que "funciona" representa un caso, mientras que "no funciona" representa muchas causas probables. Si lo pensamos de esa manera, haber 0evaluado truey el resto falsehabría tenido mucho más sentido.

Conclusión

Mi conclusión es esencialmente mi pregunta original: ¿por qué diseñamos lenguajes donde 0está falsey los demás valores true, teniendo en cuenta mis pocos ejemplos anteriores y tal vez algunos más en los que no pensé?

Seguimiento: Es bueno ver que hay muchas respuestas con muchas ideas y tantas razones posibles para que sea así. Me encanta lo apasionado que pareces ser al respecto. Originalmente hice esta pregunta por aburrimiento, pero como pareces tan apasionado, decidí ir un poco más allá y preguntar sobre la razón detrás de la elección booleana para 0 y 1 en Math.SE :)

Morwenn
fuente
32
strcmp()No es un buen ejemplo para verdadero o falso, ya que devuelve 3 valores diferentes. Y se sorprenderá cuando comience a usar un shell, donde 0 significa verdadero y cualquier otra cosa significa falso.
ott--
52
@ ott--: en shells de Unix, 0 significa éxito y no cero significa falla , no exactamente lo mismo que "verdadero" y "falso".
Keith Thompson
14
@KeithThompson: En Bash (y otros shells), "éxito" y "fracaso" realmente son lo mismo que "verdadero" y "falso". Considere, por ejemplo, la declaración if true ; then ... ; fi, donde truees un comando que devuelve cero y esto le dice ifque se ejecute ....
ruakh
13
No hay booleanos en el hardware, solo números binarios, y en la mayoría de los ISA históricos, un número distinto de cero se considera "verdadero" en todas las instrucciones de ramificación condicional (a menos que estén usando banderas). Por lo tanto, los lenguajes de bajo nivel están obligados a seguir las propiedades de hardware subyacentes.
SK-logic
2
@MasonWheeler Tener un tipo booleano no implica nada. Por ejemplo pitón hace tener un booltipo pero las comparaciones / etc si las condiciones pueden tener cualquier valor de retorno.
Bakuriu

Respuestas:

98

0es falseporque ambos son elementos cero en semirrelaciones comunes . Aunque son tipos de datos distintos, tiene sentido intuitivo convertirlos porque pertenecen a estructuras algebraicas isomorfas.

  • 0es la identidad para la suma y cero para la multiplicación. Esto es cierto para números enteros y racionales, pero no para números de punto flotante IEEE-754: 0.0 * NaN = NaNy 0.0 * Infinity = NaN.

  • falsees la identidad para Boolean xor (⊻) y cero para Boolean y (∧). Si los booleanos se representan como {0, 1} —el conjunto de enteros módulo 2— puede pensar en ⊻ como suma sin acarreo y ∧ como multiplicación.

  • ""y []son identidad para concatenación, pero hay varias operaciones para las cuales tienen sentido como cero. La repetición es una, pero la repetición y la concatenación no se distribuyen, por lo que estas operaciones no forman un semired.

Tales conversiones implícitas son útiles en programas pequeños, pero en general pueden hacer que los programas sean más difíciles de razonar. Solo una de las muchas compensaciones en el diseño del lenguaje.

Jon Purdy
fuente
1
Es bueno que hayas mencionado listas. (Por cierto, niles tanto la lista vacía []como el falsevalor en Common Lisp; ¿hay una tendencia a fusionar identidades de diferentes tipos de datos?) Todavía tiene que explicar por qué es natural considerar falso como identidad aditiva y verdadero como identidad multiplicativa. y no al revés. ¿No es posible considerar truecomo la identificación ANDy cero para OR?
Giorgio
3
+1 para referirse a identidades similares. Finalmente, una respuesta que no se reduce a "convención, lidiar con ella".
l0b0
55
+1 por dar detalles de una matemática concreta y muy antigua en la que esto se ha seguido y tiene sentido desde hace mucho tiempo
Jimmy Hoffa
1
Esta respuesta no tiene sentido. truees también la identidad y el cero de semirrelaciones (booleanas y / o). No hay ninguna razón, aparte de la convención, para considerar que falseesté más cerca de 0 que true.
TonioElGringo
1
@TonioElGringo: La diferencia entre verdadero y falso es la diferencia entre XOR y XNOR. Se pueden formar anillos isomórficos usando AND / XOR, donde verdadero es la identidad multiplicativa y falso el aditivo, o con OR y XNOR, donde falso es la identidad multiplicativa y verdadero es el aditivo, pero XNOR no se considera como común. operación fundamental como es XOR.
supercat
74

Porque las matemáticas funcionan.

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

... insert many other examples here.

Tradicionalmente, los programas C tienen condiciones como

if (someFunctionReturningANumber())

más bien que

if (someFunctionReturningANumber() != 0)

porque el concepto de cero es equivalente a falso es bien entendido.

Robert Harvey
fuente
21
Los idiomas están diseñados así porque las matemáticas tienen sentido. Eso vino primero.
Robert Harvey
26
@ Morwenn, se remonta al siglo XIX y George Boole. La gente ha estado representando False como 0 y True como! 0 por más tiempo que ha habido computadoras.
Charles E. Grant
11
No veo por qué las matemáticas no funcionan de otra manera si simplemente cambias todas las definiciones para que AND sea + y OR sea *.
Neil G
77
Exactamente: las matemáticas funcionan en ambos sentidos y la respuesta a esta pregunta parece ser que es puramente convencional.
Neil G
66
@Robert Sería genial si pudieras deletrear los "fundamentos matemáticos" en tu publicación.
phant0m
38

Como otros han dicho, las matemáticas fueron primero. Es por eso que 0 es falsey 1 es true.

¿De qué matemáticas estamos hablando? Álgebras booleanas que datan de mediados de 1800, mucho antes de que aparecieran las computadoras digitales.

También se podría decir que la convención surgió de una lógica proposicional , que incluso es más antigua que las álgebras booleanas. Esta es la formalización de muchos de los resultados lógicos que los programadores conocen y aman ( false || xigual x, true && xigual , xetc.).

Básicamente estamos hablando de aritmética en un conjunto con dos elementos. Piensa en contar en binario. Las álgebras booleanas son el origen de este concepto y su base teórica. Las convenciones de lenguajes como C son solo una aplicación sencilla.

joshin4colours
fuente
2
Podrías, seguro. Pero mantenerlo en la forma "estándar" encaja bien con la aritmética general (0 + 1 = 1, no 0 + 1 = 0).
joshin4colours
2
Sí, pero presumiblemente escribiría AND con + y OR con * si invierte las definiciones también.
Neil G
3
Las matemáticas no vinieron primero. Math reconoció que 0 y 1 forman un campo, en el cual AND es como la multiplicación y OR es como la suma.
Kaz
1
@Kaz: Pero {0, 1} con OR y AND no forma un campo.
Giorgio
2
Me molesta un poco que más respuestas y comentarios digan eso true = 1. Eso no es del todo exacto, porque true != 0no es exactamente lo mismo. Una razón (no la única) por la que uno debe evitar comparaciones como if(something == true) { ... }.
JensG
26

Pensé que esto tenía que ver con la "herencia" de la electrónica, y también con el álgebra booleana, donde

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

strcmp devuelve 0 cuando las cadenas son iguales tiene que ver con su implementación, ya que lo que realmente hace es calcular la "distancia" entre las dos cadenas. Que 0 también se considere falso es solo una coincidencia.

devolver 0 en caso de éxito tiene sentido porque 0 en este caso se usa para indicar que no hay error y cualquier otro número sería un código de error. Usar cualquier otro número para el éxito tendría menos sentido ya que solo tiene un único código de éxito, mientras que puede tener varios códigos de error. Usas "¿Funcionó?" como la expresión de instrucción if y decir 0 = sí tendría más sentido, pero la expresión es más correcta "¿Algo salió mal?" y luego ves que 0 = no tiene mucho sentido. Pensar en false/trueesto no tiene sentido aquí, ya que en realidad lo es no error code/error code.

Svish
fuente
Jaja, usted es el primero en formular la pregunta de error de retorno explícitamente. Ya sabía que lo interpretaba a mi manera y podría preguntarse de otra manera, pero tú eres el primero en expresarlo explícitamente (de las muchas respuestas y comentarios). En realidad, no diría que una u otra forma no tiene sentido, pero más que ambas tienen sentido de diferentes maneras :)
Morwenn
1
De hecho, diría 0que success/no errores lo único que tiene sentido cuando otros enteros representan códigos de error. Eso 0también sucede falseen otros casos, en realidad no importa, ya que no estamos hablando de verdadero o falso aquí;)
Svish
Tuve la misma idea, así que
subí
1
Su punto sobre el strcmp()cálculo de la distancia es bastante bueno. Si se hubiera llamado strdiff(), if (!strdiff())sería muy lógico.
Kevin Cox
"electrónica [...] donde 0 = [...] falso, 1 = [...] verdadero" - incluso en electrónica, esto es solo una convención , y no es la única. Llamamos a esto lógica positiva, pero también puede usar lógica negativa, donde un voltaje positivo indica falso y negativo indica verdadero. Luego, el circuito que usarías para AND se convierte en OR, OR se convierte en AND, y así sucesivamente. Debido a la ley de De Morgan, todo termina siendo equivalente. A veces, encontrará parte de un circuito electrónico implementado en lógica negativa por conveniencia, en cuyo punto los nombres de las señales en esa parte se anotan con una barra encima de ellos.
Julio
18

Como se explica en este artículo , los valores falsey trueno deben confundirse con los enteros 0 y 1, sino que pueden identificarse con los elementos del campo de Galois (campo finito) de dos elementos (ver aquí ).

Un campo es un conjunto con dos operaciones que satisfacen ciertos axiomas.

Los símbolos 0 y 1 se usan convencionalmente para denotar las identidades aditiva y multiplicativa de un campo porque los números reales también son un campo (pero no finito) cuyas identidades son los números 0 y 1.

La identidad aditiva es el elemento 0 del campo, de modo que para todas las x:

x + 0 = 0 + x = x

y la identidad multiplicativa es el elemento 1 del campo, de modo que para todo x:

x * 1 = 1 * x = x

El campo finito de dos elementos tiene solo estos dos elementos, a saber, la identidad aditiva 0 (o false) y la identidad multiplicativa 1 (o true). Las dos operaciones de este campo son el XOR lógico (+) y el AND lógico (*).

Nota. Si voltea las operaciones (XOR es la multiplicación y AND es la suma), entonces la multiplicación no es distributiva sobre la suma y ya no tiene un campo. En tal caso, no tiene ninguna razón para llamar a los dos elementos 0 y 1 (en cualquier orden). Tenga en cuenta también que no puede elegir la operación OR en lugar de XOR: no importa cómo interprete OR / AND como suma / multiplicación, la estructura resultante no es un campo (no todos los elementos inversos existen como lo requieren los axiomas del campo).

En cuanto a las funciones C:

  • Muchas funciones devuelven un número entero que es un código de error. 0 significa SIN ERROR.
  • Intuitivamente, la función strcmpcalcula la diferencia entre dos cadenas. 0 significa que no hay diferencia entre dos cadenas, es decir, que dos cadenas son iguales.

Las explicaciones intuitivas anteriores pueden ayudar a recordar la interpretación de los valores de retorno, pero es aún más fácil simplemente verificar la documentación de la biblioteca.

Giorgio
fuente
1
+1 por mostrar que si intercambias estas arbitrariamente, las matemáticas ya no funcionan.
Jimmy Hoffa
2
Volteado: dado un campo con dos elementos y operaciones * y +, identificamos True con 0 y False con 1. Identificamos OR con * y XOR con +.
Neil G
1
Encontrará que ambas identificaciones se realizan en el mismo campo y ambas son consistentes con las reglas de la lógica booleana. Su nota es lamentablemente incorrecta :)
Neil G
1
Si supone que True = 0, y XOR es +, entonces True debe ser la identidad de XOR. Pero no es porque True XOR True = False. A menos que redefina la operación XOR en True para que True XOR True = True. Entonces, por supuesto, su construcción funciona porque acaba de cambiar el nombre de las cosas (en cualquier estructura matemática siempre puede hacer una permutación de nombre con éxito y obtener una estructura isomórfica). Por otro lado, si dejas que Verdadero, Falso y XOR tengan su significado habitual, entonces Verdadero XOR Verdadero = Falso y Verdadero no puede ser la identidad aditiva, es decir, Verdadero no puede ser 0.
Giorgio
1
@Giorgio: Corrigí mi construcción según tu comentario en mi último comentario ...
Neil G
13

Debe considerar que los sistemas alternativos también pueden ser decisiones de diseño aceptables.

Shells: el estado de salida 0 es verdadero, el no cero es falso

Ya se ha mencionado el ejemplo de shells que tratan un estado de salida 0 como verdadero.

$ ( 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 razón es que hay una forma de tener éxito, pero muchas formas de fracasar, por lo que usar 0 como valor especial que significa "sin errores" es pragmático.

Ruby: 0 es como cualquier otro número

Entre los lenguajes de programación "normales", hay algunos valores atípicos, como Ruby, que tratan el 0 como un valor verdadero.

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

La razón es que solo eso falsey nildebe ser falso. Para muchos novatos de Ruby, es una trampa. Sin embargo, en algunos casos, es bueno que 0 se trate como cualquier otro 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"

Sin embargo, dicho sistema solo funciona en un lenguaje que puede distinguir booleanos como un tipo separado de los números. En los primeros días de la informática, los programadores que trabajaban con lenguaje ensamblador o lenguaje de máquina sin procesar no tenían tales lujos. Probablemente sea natural tratar a 0 como el estado "en blanco" y establecer un bit a 1 como un indicador cuando el código detecta que algo sucedió. Por extensión, la convención desarrolló que el cero se trataba como falso, y los valores distintos de cero se consideraron verdaderos. Sin embargo, no tiene por qué ser así.

Java: los números no pueden tratarse como booleanos en absoluto

En Java, truey falseson los únicos valores booleanos. Los números no son booleanos, y ni siquiera se pueden convertir en booleanos ( especificación del lenguaje Java, sección 4.2.2 ):

No hay conversiones entre los tipos integrales y el tipo boolean.

Esa regla simplemente evita la pregunta por completo: todas las expresiones booleanas deben escribirse explícitamente en el código.

200_success
fuente
1
¡Rebol y Red tratan a INTEGER con valor 0! valores como verdadero, y tener un NINGUNO separado! tipo (con un solo valor, NINGUNO) tratado como falso condicional además de LOGIC! falso. He encontrado una frustración significativa al intentar escribir código JavaScript que trata 0 como falso; Es una decisión increíblemente torpe para un lenguaje de tipo dinámico. Si quieres probar algo que puede ser nulo o 0 terminas teniendo que escribir if (thing === 0), eso no es genial.
HostileFork
@HostileFork No lo sé. Me parece que tiene sentido que 0sea true(como cualquier otro número entero) en un lenguaje dinámico. A veces me encontraba atrapando un 0cuando intentaba atrapar Noneen Python, y eso a veces puede ser bastante difícil de detectar.
Morwenn
2
Ruby no es un caso atípico. Ruby toma esto de Lisp (Ruby incluso se llama secretamente "MatzLisp"). Lisp es un lenguaje convencional en informática. El cero es también sólo un verdadero valor en el shell POSIX, porque es una parte del texto: if [ 0 ] ; then echo this executes ; fi. El valor de datos falsos es una cadena vacía, y una falsedad comprobable es un estado de terminación fallido de un comando, que está representado por un valor distinto de cero.
Kaz
8

Antes de abordar el caso general, podemos analizar sus contraejemplos.

Comparaciones de cadenas

Lo mismo vale para muchos tipos de comparaciones, en realidad. Tales comparaciones calculan una distancia entre dos objetos. Cuando los objetos son iguales, la distancia es mínima. Entonces, cuando la "comparación tiene éxito", el valor es 0. Pero en realidad, el valor de retorno de nostrcmp es un valor booleano, es una distancia, y eso es lo que atrapa a los programadores inconscientes .if (strcmp(...)) do_when_equal() else do_when_not_equal()

En C ++ podríamos rediseñar strcmppara devolver un Distanceobjeto, que anula operator bool()para devolver verdadero cuando 0 (pero luego sería mordido por un conjunto diferente de problemas). O en C simple, solo tiene una streqfunción que devuelve 1 cuando las cadenas son iguales, y 0 en caso contrario.

Llamadas API / código de salida del programa

Aquí te importa la razón por la que algo salió mal, porque esto impulsará las decisiones por error. Cuando las cosas tienen éxito, no quieres saber nada en particular: tu intención se realiza. Por lo tanto, el valor de retorno debe transmitir esta información. Es no un valor lógico, es un código de error. El valor de error especial 0 significa "sin error". El resto del rango representa errores localmente significativos con los que tiene que lidiar (incluido 1, que a menudo significa "error no especificado").

Caso general

Esto nos deja con la pregunta: ¿por qué son los valores booleanos Truey Falsecomúnmente representado con 1 y 0, respectivamente?

Bueno, además del argumento subjetivo "se siente mejor de esta manera", aquí hay algunas razones (subjetivas también) en las que puedo pensar:

  • analogía del circuito eléctrico. La corriente está encendida durante 1s y apagada durante 0s. Me gusta tener (1, Sí, Verdadero, Encendido) juntos y (0, No, Falso, Apagado), en lugar de otra combinación

  • Inicializaciones de memoria. Cuando tengo memset(0)un montón de variables (ya sean ints, flotantes, bools), quiero que su valor coincida con los supuestos más conservadores. Por ejemplo, mi suma es inicialmente 0, el predicado es falso, etc.

Quizás todas estas razones están ligadas a mi educación: si me hubieran enseñado a asociar 0 con Verdadero desde el principio, optaría por lo contrario.

usuario44761
fuente
2
En realidad, hay al menos un lenguaje de programación que trata 0 como verdadero. El caparazón de Unix.
Jan Hudec
+1 para abordar el problema real: la mayor parte de la pregunta de Morwenn no se trata boolen absoluto.
dan04
@ dan04 lo es. Todo el post es sobre las razones de la elección del elenco de intque boolen muchos lenguajes de programación. La comparación y el material de gestión de errores son solo ejemplos de lugares donde tendría sentido emitirlo de una manera diferente a la que se hace actualmente.
Morwenn
6

Desde una perspectiva de alto nivel, estás hablando de tres tipos de datos bastante diferentes:

  1. Un booleano. La convención matemática en álgebra booleana es usar 0 para falsey 1 para true, por lo que tiene sentido seguir esa convención. Creo que de esta manera también tiene más sentido intuitivamente.

  2. El resultado de la comparación. Esto tiene tres valores: <, =y >(aviso de que ninguno de ellos es true). Para ellos tiene sentido usar los valores de -1, 0 y 1, respectivamente (o, más generalmente, un valor negativo, cero y un valor positivo).

    Si desea verificar la igualdad y solo tiene una función que realiza una comparación general, creo que debería hacerlo explícito utilizando algo como strcmp(str1, str2) == 0. Me resulta !confuso usarlo en esta situación, porque trata un valor no booleano como si fuera un valor booleano.

    Además, tenga en cuenta que la comparación y la igualdad no tienen que ser lo mismo. Por ejemplo, si ordena a las personas por su fecha de nacimiento, Compare(me, myTwin)debería regresar 0, pero Equals(me, myTwin)debería regresar false.

  3. El éxito o el fracaso de una función, posiblemente también con detalles sobre ese éxito o fracaso. Si está hablando de Windows, entonces se llama a este tipo HRESULTy un valor distinto de cero no necesariamente indica falla. De hecho, un valor negativo indica falla y éxito no negativo. El valor de éxito es muy frecuente S_OK = 0, pero también puede ser, por ejemplo S_FALSE = 1, u otros valores.

La confusión proviene del hecho de que tres tipos de datos lógicamente diferentes se representan realmente como un tipo de datos único (un entero) en C y algunos otros lenguajes y que puede usar un entero en una condición. Pero no creo que tenga sentido redefinir booleano para simplificar el uso de algunos tipos no booleanos en condiciones.

Además, considere otro tipo que a menudo se usa en una condición en C: un puntero. Allí, es natural tratar un NULLpuntero (que se representa como 0) como false. Por lo tanto, seguir su sugerencia también dificultaría el trabajo con punteros. (Aunque, personalmente, prefiero comparar explícitamente los punteros con NULL, en lugar de tratarlos como booleanos).

svick
fuente
4

Cero puede ser falso porque la mayoría de las CPU tienen un indicador CERO que se puede utilizar para bifurcar. Guarda una operación de comparación.

Veamos por qué.

Algunos psuedocode, ya que la audiencia probablemente no lee el ensamblado

c-source bucle simple llamadas wibble 10 veces

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

algunos pretenden asamblea para eso

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- fuente otro bucle simple llama wibble 10 veces

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

alguna asamblea simulada 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  

alguna fuente más c

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

y la asamblea

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

¿Ves lo corto que es eso?

alguna fuente más c

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

y el ensamblaje (supongamos un compilador marginalmente inteligente que puede reemplazar == 0 sin comparar)

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

Ahora intentemos una convención de verdadero = 1

algo más c source #define TRUE 1 int foo = TRUE; if (foo == TRUE) wibble ()

y la asamblea

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

¿Ves cuán corto es el caso con non-zero true?

Las primeras CPU tenían pequeños conjuntos de banderas unidas al acumulador.

Para verificar si a> bo a = b generalmente toma una instrucción de comparación.

  • A menos que B sea CERO, en cuyo caso el indicador CERO se establece Implementado como un NOR lógico simple o todos los bits en el Acumulador.
  • O NEGATIVO en el que simplemente use el "bit de signo", es decir, el bit más significativo del acumulador si está utilizando la aritmética del complemento a dos. (Principalmente lo hacemos)

Vamos a repetir esto. En algunas CPU antiguas no tenía que usar una instrucción de comparación para el acumulador igual a CERO, o un acumulador menor que cero.

¿Ahora ves por qué cero podría ser falso?

Tenga en cuenta que este es el código psuedo y ningún conjunto de instrucciones real se parece a esto. Si conoces el ensamblaje, sabes que estoy simplificando mucho las cosas aquí. Si sabes algo sobre el diseño del compilador, no necesitas leer esta respuesta. Cualquiera que sepa algo sobre el desenrollamiento de bucles o la predicción de ramificaciones, la clase avanzada está al final del pasillo en la habitación 203.

Tim Williscroft
fuente
2
Su punto no está bien hecho aquí porque por una cosa if (foo)y if (foo != 0)debería generar el mismo código, y en segundo lugar, que está mostrando que el lenguaje ensamblador que está utilizando, de hecho, tiene operandos booleanos explícitas y ensayos para ellos. Por ejemplo jzsignifica jump if zero. En otras palabras if (a == 0) goto target;. Y la cantidad ni siquiera se está probando directamente; la condición se convierte en un indicador booleano que se almacena en una palabra de máquina especial. En realidad es más comocpu.flags.zero = (a == 0); if (cpu.flags.zero) goto target;
Kaz
No Kaz, las CPU más antiguas no funcionaban así. El jz / jnz se puede realizar sin hacer una instrucción de comparación. Lo que realmente era el punto de mi publicación completa.
Tim Williscroft
2
No escribí nada sobre una instrucción de comparación.
Kaz
¿Puedes citar un procesador que tenga una jzinstrucción pero no jnz? (o cualquier otro conjunto asimétrico de instrucciones condicionales)
Toby Speight
3

Hay muchas respuestas que sugieren que la correspondencia entre 1 y verdadero es necesaria por alguna propiedad matemática. No puedo encontrar ninguna de esas propiedades y sugerir que es una convención puramente histórica.

Dado un campo con dos elementos, tenemos dos operaciones: suma y multiplicación. Podemos mapear operaciones booleanas en este campo de dos maneras:

Tradicionalmente, identificamos True con 1 y False con 0. Identificamos AND con * y XOR con +. Por lo tanto, OR es una adición saturante.

Sin embargo, podríamos identificar fácilmente Verdadero con 0 y Falso con 1. Luego identificamos OR con * y XNOR con +. Por lo tanto, AND es una adición saturante.

Neil G
fuente
44
Si hubiera seguido el enlace en wikipedia, podría haber descubierto que el concepto de álgebra booleana está cerrado en relación con el de un campo de Galois de dos elementos ( en.wikipedia.org/wiki/GF%282%29 ). Los símbolos 0 y 1 se usan convencionalmente para denotar las identidades aditiva y multiplicativa, respectivamente, porque los números reales también son un campo cuyas identidades son los números 0 y 1.
Giorgio
1
@NeilG Creo que Giorgio está tratando de decir que es más que una simple convención. 0 y 1 en álgebra booleana son básicamente lo mismo que 0 y 1 en GF (2), que se comportan casi igual que 0 y 1 en números reales con respecto a la suma y la multiplicación.
svick
1
@svick: No, porque simplemente puedes renombrar la multiplicación y la suma de saturación para que sea OR y AND y luego voltear las etiquetas para que 0 sea Verdadero y 1 sea Falso. Giorgio dice que fue una convención de la lógica booleana, que se adoptó como una convención de la informática.
Neil G
1
@Neil G: No, no puede voltear + y * y 0 y 1 porque un campo requiere distributividad de la multiplicación sobre la suma (consulte en.wikipedia.org/wiki/Field_%28mathematics%29 ), pero si establece +: = AND y *: = XOR, obtienes T XOR (T AND F) = T XOR F = T, mientras que (T XOR T) AND (T XOR F) = F AND T = F. Por lo tanto, volteando las operaciones y las identidades ya no tienes un campo. Entonces, la OMI que define 0 y 1 como las identidades de un campo apropiado parece capturar falso y verdadero con bastante fidelidad.
Giorgio
1
@ giorgio: he editado la respuesta para que sea obvio lo que está sucediendo.
Neil G
3

Curiosamente, cero no siempre es falso.

En particular, la convención de Unix y Posix es definir EXIT_SUCCESScomo 0 (y EXIT_FAILUREcomo 1). En realidad, ¡es incluso una convención estándar de C !

Entonces, para shells de Posix y syscalls de salida (2) , 0 significa "exitoso", que intuitivamente es más verdadero que falso.

En particular, el shell ifquiere un retorno del proceso EXIT_SUCCESS(que es 0) para seguir su rama "entonces".

En Scheme (pero no en Common Lisp o en MELT ) 0 y nil (es decir, ()en Scheme) son verdaderos, ya que el único valor falso es#f

Estoy de acuerdo, ¡estoy jugando!

Basile Starynkevitch
fuente
3

C se utiliza para la programación de bajo nivel cerca del hardware, un área en la que a veces es necesario cambiar entre operaciones lógicas y bit a bit en los mismos datos. La obligación de convertir una expresión numérica a booleana solo para realizar una prueba desordenaría el código.

Puedes escribir cosas como:

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

más bien que

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

En un ejemplo aislado, no es tan malo, pero tener que hacerlo será molesto.

Asimismo, conversar operaciones. Es útil para el resultado de una operación booleana, como una comparación, producir un 0 o 1: supongamos que queremos establecer el tercer bit de alguna palabra en función de si modemctrlel portador detecta el bit:

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

Aquí tenemos que tener != 0, para reducir el resultado de la &expresión biwise a 0o 1, pero debido a que el resultado es solo un número entero, nos ahorramos tener que agregar algo de molesto elenco para convertir aún más el valor booleano en número entero.

A pesar de que la C moderna ahora tiene un booltipo, aún conserva la validez de un código como este, tanto porque es algo bueno como por la rotura masiva con compatibilidad con versiones anteriores que se causaría de otra manera.

Otro ejemplo donde C es resbaladizo: probar dos condiciones booleanas como un interruptor de cuatro vías:

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

¡No podrías quitarle esto al programador C sin luchar!

Por último, C a veces sirve como una especie de lenguaje ensamblador de alto nivel. En lenguajes ensambladores, tampoco tenemos tipos booleanos. Un valor booleano es solo un bit o un cero frente a un valor distinto de cero en una ubicación o registro de memoria. Un número entero cero, cero booleano y la dirección cero se prueban de la misma manera en conjuntos de instrucciones en lenguaje ensamblador (y quizás incluso en coma flotante cero). La semejanza entre C y el lenguaje ensamblador es útil, por ejemplo, cuando C se usa como el idioma de destino para compilar otro idioma (¡incluso uno que ha marcado booleanos!)

Kaz
fuente
0

Un valor booleano o de verdad solo tiene 2 valores. Verdadero y falso.

Estos no deben representarse como enteros, sino como bits (0 y 1).

Decir que cualquier otro número entero al lado de 0 o 1 no es falso es una declaración confusa. Las tablas de verdad tratan con valores de verdad, no enteros.

Desde un valor de verdad prospectivo, -1 o 2 romperían todas las tablas de verdad y cualquier lógica booleana asociada con ellas.

  • 0 Y -1 ==?!
  • 0 O 2 ==?!

La mayoría de los idiomas generalmente tienen un booleantipo que cuando se convierte a un tipo de número como entero revela falso para ser lanzado como un valor entero de 0.

Jon Raynor
fuente
1
0 Y -1 == cualquier valor booleano al que los eches. De eso se trata mi pregunta, por qué enviarlos TRUEo FALSE. Nunca dije, tal vez lo hice, pero no fue intencionado, los enteros eran verdaderos o falsos, pregunté por qué evalúan a cualquiera cuando se convierten en booleanos.
Morwenn
-6

En última instancia, está hablando de romper el lenguaje principal porque algunas API son malas. Las API de mierda no son nuevas, y no puede solucionarlas rompiendo el idioma. Es un hecho matemático que 0 es falso y 1 es verdadero, y cualquier lenguaje que no respete esto está fundamentalmente roto. La comparación de tres vías es un nicho y no tiene nada que ver con que su resultado se convierta implícitamente, boolya que arroja tres resultados posibles. Las antiguas API de C simplemente tienen un manejo de errores terrible y también están limitadas porque C no tiene las características de lenguaje necesarias para no tener interfaces terribles.

Tenga en cuenta que no estoy diciendo eso para los idiomas que no tienen conversión integer-> booleana implícita.

DeadMG
fuente
11
"Es un hecho matemático que 0 es falso y 1 es verdadero" Erm.
R. Martinho Fernandes
11
¿Puedes citar una referencia para tu "hecho matemático de que 0 es falso y 1 es verdadero"? Tu respuesta suena peligrosamente como una diatriba.
Dan Pichelman
14
No es un hecho matemático, pero ha sido una convención matemática desde el siglo XIX.
Charles E. Grant
1
El álgebra booleana está representada por un campo finito en el que 0 y 1 son los elementos de identidad para operaciones que se parecen a la suma y la multiplicación. Esas operaciones son, respectivamente, OR y AND. De hecho, el álgebra booleana se escribe de manera muy similar al álgebra normal, donde la yuxtaposición denota AND, y el +símbolo denota OR. Así, por ejemplo, abc + a'b'csignifica (a and b and c) or (a and (not b) and (not c)).
Kaz