Почему 0 ложно?

115

Этот вопрос может показаться глупым, но почему 0оценки falseи любое другое [целочисленное] значение для trueбольшинства языков программирования составляют?

Сравнение строк

Поскольку вопрос кажется немного слишком простым, я объясню немного подробнее: во-первых, это может показаться очевидным любому программисту, но почему бы не появиться язык программирования - на самом деле может быть, но не любой Я использовал - где 0вычисляет trueи все другие [целочисленные] значения false? Это одно замечание может показаться случайным, но у меня есть несколько примеров, где это могло бы быть хорошей идеей. Прежде всего, давайте возьмем пример трехстороннего сравнения строк, я возьму в strcmpкачестве примера C : любой программист, пытающийся использовать C в качестве своего первого языка, может испытывать желание написать следующий код:

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

Так как strcmpвозвращает, 0которое оценивает, falseкогда строки равны, то, что пытался сделать начинающий программист, с треском проваливается, и он, как правило, сначала не понимает, почему. Если бы 0оценены , чтобы trueвместо того, чтобы эта функция могла быть использована в наиболее простом выражении - один выше - при сравнении равенства, а соответствующие проверки на -1и 1было бы сделано только тогда , когда это необходимо. Мы бы рассматривали тип возвращаемого значения bool( как мы думаем ) большую часть времени.

Кроме того, давайте введем новый тип sign, который просто принимает значения -1, 0и 1. Это может быть очень удобно. Представьте себе, что в C ++ есть оператор космического корабля, и мы его хотим std::string(ну, там уже есть compareфункция, но оператор космического корабля более интересен). Декларация в настоящее время будет следующей:

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

Если бы это 0было оценено true, оператор космического корабля даже не существовал бы, и мы могли бы объявить operator==так:

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

Это operator==бы обрабатывало трехстороннее сравнение одновременно и могло бы использоваться для выполнения следующей проверки, в то же время имея возможность проверить, какая строка лексикографически превосходит другую при необходимости:

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

Старая обработка ошибок

Теперь у нас есть исключения, так что эта часть относится только к старым языкам, где такого не существует (например, C). Если мы посмотрим на стандартную библиотеку C (и POSIX), то увидим, что функции maaaaany возвращаются в 0случае успеха и любое целое число в противном случае. Я, к сожалению, видел, как некоторые люди делают такие вещи:

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

Если мы думаем о том, как мы думаем в программировании, мы часто имеем следующую модель рассуждений:

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

Если думать об этом снова, это имело бы смысл ставить только нейтральное значение, 0, к yes(и это, как функции C работают), в то время как все остальные значения могут быть там , чтобы решить многие случаи no. Тем не менее, во всех языках программирования, которые я знаю (за исключением, может быть, некоторых экспериментальных эзотерических языков), который yesоценивается falseкак ifусловие, а все noслучаи - как условие true. Есть много ситуаций, когда «это работает» представляет один случай, в то время как «это не работает» представляет много вероятных причин. Если мы думаем об этом таким образом, то иметь 0оценку trueи все остальное falseимело бы гораздо больше смысла.

Заключение

По сути, мой вывод - это мой первоначальный вопрос: почему мы разрабатывали языки там, где 0есть falseи другие значения true, принимая во внимание мои несколько приведенных выше примеров и, может быть, некоторые другие, о которых я не думал?

Продолжение: Приятно видеть, что есть много ответов с множеством идей и как можно больше причин для этого. Мне нравится, насколько ты увлечен этим. Изначально я задавал этот вопрос от скуки, но, поскольку вы кажетесь настолько страстным, я решил пойти немного дальше и спросить об обосновании логического выбора для 0 и 1 на Math.SE :)

Morwenn
источник
32
strcmp()плохой пример для истины или ложи, так как он возвращает 3 разных значения. И вы будете удивлены, когда начнете использовать оболочку, где 0 означает true, а все остальное означает false.
ot--
52
@ ott--: В оболочках Unix 0 означает успех, а ненулевой означает неудачу - не совсем то же самое, что «истина» и «ложь».
Кит Томпсон
14
@KeithThompson: В Bash (и других оболочках) «успех» и «провал» действительно совпадают с «истиной» и «ложью». Рассмотрим, например, оператор if true ; then ... ; fi, где true- это команда, которая возвращает ноль, и это указывает ifна выполнение ....
Руах
13
В аппаратном обеспечении нет никаких логических значений, только двоичные числа, и в большинстве исторических ISA ненулевое число считается «истинным» во всех инструкциях условного ветвления (если только они не используют флаги вместо этого). Таким образом, языки низкого уровня непременно должны следовать базовым аппаратным свойствам.
SK-logic
2
@MasonWheeler Наличие логического типа ничего не значит. Например питон действительно имеет boolтипа , но сравнения / , если условия и т.д. могут иметь любое возвращаемое значение.
Бакуриу

Ответы:

98

0это falseпотому , что они оба нулевые элементы в общих полукольцами . Несмотря на то, что они являются различными типами данных, имеет смысл интуитивно понимать преобразование между ними, поскольку они принадлежат изоморфным алгебраическим структурам.

  • 0это тождество для сложения и ноль для умножения. Это верно для целых и рациональных чисел, но не для чисел с плавающей точкой IEEE-754: 0.0 * NaN = NaNи 0.0 * Infinity = NaN.

  • falseявляется тождеством для логического xor (⊻) и нуля для логического и (∧). Если логические значения представлены как {0, 1} - набор целых чисел по модулю 2 - вы можете рассматривать ⊻ как сложение без переноса и ∧ как умножение.

  • ""и []являются идентичностью для объединения, но есть несколько операций, для которых они имеют смысл как ноль. Повторение одно, но повторение и конкатенация не распределяются, поэтому эти операции не образуют полукольца.

Такие неявные преобразования полезны в небольших программах, но в целом могут затруднить анализ программ. Просто один из многих компромиссов в языковом дизайне.

Джон Перди
источник
1
Приятно, что вы упомянули списки. (Кстати, nilэто и пустой список, []и falseзначение в Common Lisp; есть ли тенденция объединять тождества из разных типов данных?) Вам все еще нужно объяснить, почему естественно рассматривать false как аддитивную идентичность и true как мультипликативную идентичность и не наоборот. Не возможно ли считать trueидентификатором для ANDи ноль для OR?
Джорджио
3
+1 за ссылки на похожие личности. Наконец, ответ, который не сводится просто к «соглашению, разберись с ним».
10
5
+1 за подробности конкретной и очень старой математики, в которой это было выполнено и долго имело смысл
Джимми Хоффа
1
Этот ответ не имеет смысла. trueтакже тождество и ноль полуколец (булево и / или). Нет никакой причины, по соглашению appart, рассмотреть falseближе к 0, чем true.
TonioElGringo
1
@TonioElGringo: Разница между истиной и ложью - это разница между XOR и XNOR. Можно сформировать изоморфные кольца, используя AND / XOR, где true - это мультипликативный тождество, а false - аддитивное, или с OR и XNOR, где false - мультипликативный тождество, а true - аддитивное, но XNOR обычно не считается общим фундаментальная операция, как XOR.
суперкат
74

Потому что математика работает.

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

... insert many other examples here.

Традиционно, программы на С имеют такие условия, как

if (someFunctionReturningANumber())

скорее, чем

if (someFunctionReturningANumber() != 0)

потому что концепция нуля, эквивалентного ложному, хорошо понятна.

Роберт Харви
источник
21
Языки разработаны так, потому что математика имеет смысл. Это было первым.
Роберт Харви
26
@ Morwenn, это восходит к 19-му веку и Джорджу Буле. Люди представляли False как 0 и True как! 0 дольше, чем были компьютеры.
Чарльз И. Грант
11
Я не понимаю, почему математика не работает иначе, если вы просто измените все определения так, чтобы AND было +, а OR было *.
Нил Дж
7
Точно: математика работает в обоих направлениях, и ответ на этот вопрос, кажется, заключается в том, что она является чисто условной.
Нил Дж
6
@ Роберт Было бы здорово, если бы вы могли изложить «математические основы» в своем посте.
phant0m
38

Как уже говорили другие, математика пришла первой. Вот почему 0 есть, falseа 1 есть true.

О какой математике мы говорим? Булевы алгебры, которые датируются серединой 1800-х годов, задолго до появления цифровых компьютеров.

Можно также сказать, что конвенция возникла из логики высказываний , которая даже старше, чем булевы алгебры. Это формализация многих логических результатов, которые программисты знают и любят ( false || xравно x, true && xравно xи так далее).

В основном мы говорим об арифметике на множестве из двух элементов. Подумайте о подсчете в двоичном виде. Булевы алгебры являются источником этой концепции и ее теоретической основой. Соглашения о языках, таких как C, являются простым приложением.

joshin4colours
источник
2
Вы могли бы, конечно. Но сохранение его «стандартным» способом хорошо вписывается в общую арифметику (0 + 1 = 1, а не 0 + 1 = 0).
joshin4colours
2
Да, но вы, вероятно, написали бы AND с + и OR с *, если бы вы также изменили определения.
Нил Дж
3
Математика не пришла первой. Математика распознала, что 0 и 1 образуют поле, в котором AND подобен умножению, а OR подобен сложению.
Каз
1
@Kaz: Но {0, 1} с OR и AND не образует поле.
Джорджио
2
Меня немного беспокоит, что больше ответов и комментариев говорят об этом true = 1. Это не совсем точно, потому true != 0что это не совсем то же самое. Одна причина (не единственная), почему следует избегать подобных сравнений if(something == true) { ... }.
JensG
26

Я думал, что это связано с «наследованием» от электроники, а также с булевой алгеброй, где

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

strcmp возвращает 0, когда строки равны, что связано с его реализацией, поскольку фактически он вычисляет «расстояние» между двумя строками. То, что 0 также считается ложным, является просто совпадением.

возвращать 0 в случае успеха имеет смысл, потому что 0 в этом случае означает отсутствие ошибок, а любое другое число будет кодом ошибки. Использование любого другого числа для успеха будет менее целесообразным, поскольку у вас есть только один код успеха, а у вас может быть несколько кодов ошибок. Вы используете "Это сработало?" как выражение оператора if и сказать 0 = да, имело бы больше смысла, но выражение более корректно: «Что-то пошло не так?» и тогда вы видите, что 0 = нет, имеет большой смысл. Думать о false/trueне имеет смысла здесь, как это на самом деле no error code/error code.

Svish
источник
Хаха, ты первый, кто прямо поставил вопрос об ошибке возврата. Я уже знал, что интерпретировал это по-своему, и это можно было бы задать другим путем, но вы первый, кто явно выразил это (из множества ответов и комментариев). На самом деле, я бы не сказал, что тот или иной путь не имеет смысла, но больше того, что оба имеют смысл по-разному :)
Morwenn
1
На самом деле я бы сказал , что 0для success/no errorэтого единственным , что имеет смысл , когда другие целые числа представляют собой коды ошибок. Это 0также означает, что falseв других случаях это не имеет большого значения, так как мы вообще не говорим об истинном или ложном;)
Свиш
У меня была та же идея, поэтому я поднял
user60812
1
Ваша точка зрения о strcmp()расчете расстояния довольно хорошая. Если бы было названо strdiff()то if (!strdiff())было бы очень логично.
Кевин Кокс
«электроника [...] где 0 = [...] ложь, 1 = [...] истина» - даже в электронике это только соглашение и не единственное. Мы называем это положительной логикой, но вы также можете использовать отрицательную логику, где положительное напряжение указывает на ложь, а отрицательное указывает на истину. Затем схема, которую вы используете для И, становится ИЛИ, ИЛИ становится И, и так далее. Из-за закона де Моргана все это оказывается эквивалентным. Иногда для удобства вы можете найти часть электронной схемы, реализованную в отрицательной логике, после чего названия сигналов в этой части отмечаются полосой над ними.
Жюль
18

Как поясняется в этой статье , значения falseи trueне следует путать с целыми числами 0 и 1, но их можно отождествлять с элементами поля Галуа (конечного поля) из двух элементов (см. Здесь ).

Поле - это набор с двумя операциями, которые удовлетворяют определенным аксиомам.

Символы 0 и 1 обычно используются для обозначения аддитивных и мультипликативных тождеств поля, поскольку действительные числа также являются полем (но не конечным), тождествами которого являются числа 0 и 1.

Аддитивным тождеством является элемент 0 поля, такой что для всех x:

x + 0 = 0 + x = x

и мультипликативная идентичность - это элемент 1 поля, такой что для всех x:

x * 1 = 1 * x = x

Конечное поле из двух элементов имеет только эти два элемента, а именно аддитивное тождество 0 (или false) и мультипликативное тождество 1 (или true). Две операции в этом поле - логический XOR (+) и логический AND (*).

Запись. Если вы переворачиваете операции (XOR - это умножение, а AND - это сложение), то умножение не является дистрибутивным по сравнению с сложением, и у вас больше нет поля. В таком случае у вас нет причин называть два элемента 0 и 1 (в любом порядке). Также обратите внимание, что вы не можете выбрать операцию ИЛИ вместо XOR: независимо от того, как вы интерпретируете ИЛИ / И как сложение / умножение, результирующая структура не является полем (не все обратные элементы существуют, как того требуют аксиомы поля).

Относительно функций C:

  • Многие функции возвращают целое число, которое является кодом ошибки. 0 означает НЕТ ОШИБКИ.
  • Интуитивно, функция strcmpвычисляет разницу между двумя строками. 0 означает, что нет никакой разницы между двумя строками, т.е. что две строки равны.

Приведенные выше интуитивные объяснения могут помочь запомнить интерпретацию возвращаемых значений, но еще проще просто проверить документацию библиотеки.

Джорджио
источник
1
+1 за показ того, что если вы произвольно поменяете их местами, математика больше не сработает.
Джимми Хоффа
2
Перевернуто: Учитывая поле с двумя элементами и операциями * и +, мы отождествляем True с 0 и False с 1. Мы идентифицируем OR с * и XOR с +.
Нил Дж
1
Вы обнаружите, что обе эти идентификации выполняются в одном и том же поле, и оба соответствуют правилам булевой логики. Ваша заметка, к сожалению, неверна :)
Нил Дж
1
Если вы предполагаете, что True = 0, а XOR равен +, то True должен быть идентификатором для XOR. Но это не потому, что True XOR True = False. Если вы не переопределяете операцию XOR на True, так что True XOR True = True. Тогда, конечно, ваша конструкция работает, потому что вы просто переименовали вещи (в любой математической структуре вы всегда можете успешно сделать перестановку имен и получить изоморфную структуру). С другой стороны, если вы позволите True, False и XOR иметь их обычное значение, то True XOR True = False и True не могут быть аддитивной идентичностью, т. Е. True не может быть 0.
Джорджио
1
@ Джорджио: я исправил свою конструкцию в соответствии с вашим комментарием в моем последнем комментарии ...
Нил Г
13

Следует учитывать, что альтернативные системы также могут быть приемлемыми проектными решениями.

Оболочки: 0 выходной статус истина, ненулевое значение ложь

Пример оболочек, рассматривающих состояние выхода 0 как true, уже упоминался.

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

Основанием для этого является то, что есть один способ добиться успеха, но есть много способов потерпеть неудачу, поэтому прагматично использовать 0 в качестве специального значения, означающего «нет ошибок».

Рубин: 0, как и любой другой номер

Среди «нормальных» языков программирования есть некоторые выбросы, такие как Ruby, которые рассматривают 0 как истинное значение.

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

Обоснованием является то , что только falseи nilдолжно быть ложным. Для многих новичков в Ruby это гоча. Однако в некоторых случаях приятно, что 0 обрабатывается так же, как и любое другое число.

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"

Однако такая система работает только на языке, который может отличать логические значения как отдельный тип от чисел. На заре вычислительной техники программисты, работающие на ассемблере или на машинном языке, не имели такой роскоши. Вероятно, вполне естественно рассматривать 0 как «пустое» состояние и устанавливать бит в 1 как флаг, когда код обнаружил, что что-то произошло. Таким образом, согласно соглашению, ноль считался ложным, а ненулевые значения считались истинными. Тем не менее, это не должно быть так.

Java: числа вообще не могут рассматриваться как логические

В Java, trueи falseявляются единственными булевы значения. Числа не являются логическими значениями и даже не могут быть преобразованы в логические значения ( спецификация языка Java, раздел 4.2.2 ):

Между целочисленными типами и типом нет приведения boolean.

Это правило просто избегает вопроса - все логические выражения должны быть явно написаны в коде.

200_success
источник
1
Rebol и Red оба относятся к 0-значному INTEGER! Значения как true и имеют отдельный NONE! type (только с одним значением, NONE) рассматривается как условное false в дополнение к LOGIC! ложный. Я обнаружил значительное разочарование при попытке написать код JavaScript, который рассматривает 0 как ложное; это невероятно неуклюжее решение для языка с динамической типизацией. Если вы хотите проверить что-то, что может быть нулевым или нулевым, вам придется написать if (thing === 0), это просто не круто.
HostileFork
@HostileFork Я не знаю. Я считаю , что это имеет смысл , что 0это true(как и любое другое целое число) в динамическом языке. Иногда мне приходилось ловить 0при попытке поймать Noneв Python, и это иногда бывает довольно трудно обнаружить.
Morwenn
2
Руби не является выбросом. Ruby берет это из Lisp (Ruby даже тайно называют «MatzLisp»). Лисп является основным языком в информатике. Ноль также только истинное значение в оболочке POSIX, потому что это часть текста: if [ 0 ] ; then echo this executes ; fi. Значение ложных данных представляет собой пустую строку, а проверяемая ложность - состояние неудачного завершения команды, которое представлено ненулевым значением.
Каз
8

Перед рассмотрением общего случая мы можем обсудить ваши контрпримеры.

Строковые сравнения

То же самое верно для многих сравнений. Такие сравнения вычисляют расстояние между двумя объектами. Когда объекты равны, расстояние минимально. Поэтому , когда «сравнение успешно», то значение равно 0. Но на самом деле, возвращаемое значение strcmpявляется не логическим значением, это расстояние, и то , что ловушки не знающие программисты делают if (strcmp(...)) do_when_equal() else do_when_not_equal().

В C ++ мы могли бы перепроектировать, strcmpчтобы вернуть Distanceобъект, который переопределяет, operator bool()чтобы возвратить true, когда 0 (но тогда вы будете укушены другим набором проблем). Или в простом C просто есть streqфункция, которая возвращает 1, когда строки равны, и 0 в противном случае.

Вызовы API / код выхода из программы

Здесь вас волнует причина, по которой что-то пошло не так, потому что это приведет к ошибкам при принятии решений. Когда все получается, вы не хотите знать ничего конкретного - ваше намерение реализовано. Поэтому возвращаемое значение должно передавать эту информацию. Это не логическое значение, это код ошибки. Значение специальной ошибки 0 означает «нет ошибки». Остальная часть диапазона представляет локально значимые ошибки, с которыми вам приходится иметь дело (включая 1, что часто означает «неопределенная ошибка»).

Общий случай

Это оставляет нас с вопросом: почему логические значения Trueи Falseобычно представлены с 1 и 0, соответственно?

Ну, кроме субъективного аргумента "так лучше", есть несколько причин (в том числе и субъективных), о которых я могу подумать:

  • аналогия электрической цепи. Ток включен в течение 1 с и выключен в течение 0 с. Мне нравится иметь (1, Да, True, On) вместе и (0, No, False, Off), а не другой микс

  • инициализация памяти. Когда я получаю memset(0)кучу переменных (будь то целые числа, числа с плавающей запятой, значения типа bools), я хочу, чтобы их значение соответствовало самым консервативным предположениям. Например, моя сумма изначально равна 0, предикат равен False и т. Д.

Возможно, все эти причины связаны с моим образованием - если бы меня с самого начала учили ассоциировать 0 с Истиной, я бы пошел другим путем.

user44761
источник
2
На самом деле есть хотя бы один язык программирования, который рассматривает 0 как истину. Оболочка Unix.
Ян Худек
+1 за решение реальной проблемы: большая часть вопроса Морвенна вообще не о чем bool.
dan04
@ dan04 Это так. Весь пост об основаниях выбора актеров из intк boolво многих языках программирования. Сравнение и анализ ошибок - это просто примеры мест, где было бы целесообразно приводить их не так, как это делается в настоящее время.
Morwenn
6

С точки зрения высокого уровня, вы говорите о трех совершенно разных типах данных:

  1. Логическое. Математическое соглашение в булевой алгебре состоит в том, чтобы использовать 0 для falseи 1 для true, поэтому имеет смысл следовать этому соглашению. Я думаю, что этот путь также имеет больше смысла интуитивно.

  2. Результат сравнения. Это имеет три значения: <, =и >(заметьте , что ни один из них не является true). Для них имеет смысл использовать значения -1, 0 и 1 соответственно (или, в более общем случае, отрицательное значение, ноль и положительное значение).

    Если вы хотите проверить на равенство, и у вас есть только функция, которая выполняет общее сравнение, я думаю, вы должны сделать это явно, используя что-то вроде strcmp(str1, str2) == 0. Я нахожу использование !в этой ситуации запутанным, потому что оно обрабатывает не логическое значение, как если бы оно было логическим.

    Также имейте в виду, что сравнение и равенство не обязательно должны совпадать. Например, если вы заказываете людей по дате их рождения, Compare(me, myTwin)должны вернуться 0, но Equals(me, myTwin)должны вернуться false.

  3. Успех или неудача функции, возможно, также с подробностями об этом успехе или неудаче. Если вы говорите о Windows, то этот тип вызывается, HRESULTи ненулевое значение не обязательно означает сбой. Фактически, отрицательное значение указывает на неудачу и неотрицательный успех. Значение успеха очень часто S_OK = 0, но это могут быть, например S_FALSE = 1, или другие значения.

Путаница возникает из-за того, что три логически совершенно разных типа данных фактически представлены как один тип данных (целое число) в C и некоторых других языках и что вы можете использовать целое число в условии. Но я не думаю, что имеет смысл переопределять логическое значение, чтобы упростить использование некоторых не-логических типов в условиях.

Также рассмотрим другой тип, который часто используется в условии в C: указатель. Там естественно считать NULL-пойнт (который представлен как 0) как false. Поэтому следование вашему предложению также затруднит работу с указателями. (Хотя лично я предпочитаю явно сравнивать указатели NULL, а не рассматривать их как логические значения.)

svick
источник
4

Ноль может быть ложным, потому что большинство процессоров имеют флаг ZERO, который можно использовать для ветвления. Это сохраняет операцию сравнения.

Посмотрим почему.

Какой-то псевдокод, так как публика, вероятно, не читает сборку

c- исходный простой цикл вызывает колебания 10 раз

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

какая-то притворная сборка для этого

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- источник еще один простой цикл вызывает колебания 10 раз

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

какая-то притворная сборка для этого случая

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  

еще немного c источником

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

и сборка

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

видите, как это коротко?

еще немного c источником

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

и сборка (предположим, что немного умный компилятор может заменить == 0 без сравнения)

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

Теперь давайте попробуем соглашение об истинности = 1

еще немного c source #define TRUE 1 int foo = TRUE; if (foo == TRUE) wibble ()

и сборка

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

видите, насколько коротким является случай с ненулевым значением true?

Действительно ранние процессоры имели небольшие наборы флагов, прикрепленных к Аккумулятору.

Чтобы проверить, что a> b или a = b, обычно требуется инструкция сравнения.

  • Если B не равен ZERO - в этом случае устанавливается флаг ZERO. Внедрен как простой логический NOR или все биты в накопителе.
  • Или ОТРИЦАТЕЛЬНЫЙ, в котором просто используйте «знаковый бит», т. Е. Самый значимый бит Аккумулятора, если вы используете арифметику с двумя дополнительными компонентами. (В основном мы делаем)

Давайте повторим это. На некоторых старых процессорах вам не приходилось использовать инструкцию сравнения для аккумулятора, равного нулю, или аккумулятора меньше нуля.

Теперь вы понимаете, почему ноль может быть ложным?

Обратите внимание, что это псевдо-код, и никакой реальный набор инструкций не выглядит так. Если вы знаете сборку, вы знаете, я здесь многое упрощаю. Если вы знаете что-нибудь о дизайне компилятора, вам не нужно было читать этот ответ. Любой, кто знает что-либо о развертывании цикла или предсказании ветвления, продвинутый класс идет по коридору в комнате 203.

Тим Виллискрофт
источник
2
Ваша точка не очень хорошо сделано здесь , потому что с одной стороны , if (foo)и if (foo != 0)должны генерировать тот же код, а во- вторых, вы показываете , что язык ассемблера вы используете на самом деле имеет явные логические операнды и тесты для них. Например, jzозначает jump if zero. Другими словами if (a == 0) goto target;. И количество даже не проверяется напрямую; условие преобразуется в логический флаг, который хранится в специальном машинном слове. Это на самом деле больше похожеcpu.flags.zero = (a == 0); if (cpu.flags.zero) goto target;
Каз
Нет, Kaz, старые процессоры не работали так. JZ / JNZ может быть выполнен без инструкции сравнения. Который был своего рода смысл всего моего поста на самом деле.
Тим Уиллискрофт
2
Я не написал ничего о инструкции по сравнению.
Каз
Можете ли вы привести процессор, у которого есть jzинструкция, но нет jnz? (или любой другой асимметричный набор условных инструкций)
Тоби Спейт
3

Есть много ответов, которые предполагают, что соответствие между 1 и истиной обусловлено некоторым математическим свойством. Я не могу найти такую ​​собственность и предполагаю, что это чисто историческое соглашение.

Для поля с двумя элементами у нас есть две операции: сложение и умножение. Мы можем отобразить логические операции с этим полем двумя способами:

Традиционно мы отождествляем True с 1 и False с 0. Мы отождествляем AND с * и XOR с +. Таким образом, OR является насыщающим дополнением.

Однако мы могли бы так же легко идентифицировать True с 0 и False с 1. Затем мы идентифицируем OR с * и XNOR с +. Таким образом, И является насыщающим дополнением.

Нил Г
источник
4
Если бы вы перешли по ссылке в Википедии, вы могли бы обнаружить, что концепция булевой алгебры замкнута и связана с концепцией поля Галуа из двух элементов ( en.wikipedia.org/wiki/GF%282%29 ). Символы 0 и 1 обычно используются для обозначения аддитивных и мультипликативных тождеств, соответственно, потому что действительные числа также являются полями, тождества которых являются числами 0 и 1.
Джорджио
1
@NeilG Я думаю, Джорджио пытается сказать, что это больше, чем просто соглашение. 0 и 1 в булевой алгебре в основном такие же, как 0 и 1 в GF (2), которые ведут себя почти так же, как 0 и 1 в действительных числах в отношении сложения и умножения.
свик
1
@svick: Нет, потому что вы можете просто переименовать умножение и насыщающее сложение в OR и AND, а затем перевернуть метки так, чтобы 0 было True, а 1 - False. Джорджио говорит, что это была конвенция булевой логики, которая была принята как конвенция информатики.
Нил Дж
1
@Neil G: Нет, вы не можете переворачивать + и * и 0 и 1, потому что поле требует дистрибутивного умножения над сложением (см. En.wikipedia.org/wiki/Field_%28matmatics%29 ), но если вы установите +: = AND и *: = XOR, вы получите T XOR (T AND F) = T XOR F = T, тогда как (T XOR T) AND (T XOR F) = F AND T = F. Поэтому, переключая операции и тождества, вы больше нет поля Таким образом, IMO, определяющий 0 и 1 в качестве идентификаторов соответствующего поля, похоже, довольно точно отражает ложь и истину.
Джорджио
1
@giorgio: я отредактировал ответ, чтобы было понятно, что происходит.
Нил Дж
3

Странно, ноль не всегда ложен.

В частности, соглашение Unix и Posix должно быть определено EXIT_SUCCESSкак 0 (и EXIT_FAILUREкак 1). На самом деле это даже стандартное соглашение C !

Таким образом, для оболочек Posix и системных вызовов exit (2) 0 означает «успешный», что наглядно скорее верно, чем ложно.

В частности, оболочка ifхочет , чтобы возвращаемый процесс EXIT_SUCCESS(то есть 0) следовал за своей ветвью "then"!

В Схеме (но не в Common Lisp или в MELT ) 0 и nil (т.е. ()в Схеме) являются истинными, поскольку единственное ложное значение#f

Я согласен, я придираюсь!

Василий Старынкевич
источник
3

C используется для низкоуровневого программирования, близкого к аппаратному, - область, в которой иногда нужно переключаться между битовыми и логическими операциями над одними и теми же данными. Необходимость преобразования числового выражения в логическое просто для выполнения теста может привести к засорению кода.

Вы можете написать такие вещи, как:

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

скорее, чем

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

В одном отдельном примере это не так уж и плохо, но необходимость делать это становится утомительной.

Аналогично, обратные операции. Для результата логической операции, такой как сравнение, полезно просто получить 0 или 1: Предположим, мы хотим установить третий бит некоторого слова в зависимости от того, modemctrlимеет ли бит обнаружения несущей:

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

Здесь мы должны иметь != 0, чтобы уменьшить результат &выражения biwise до 0или 1, но поскольку результатом является просто целое число, мы избавлены от необходимости добавлять некоторое раздражающее приведение для дальнейшего преобразования логического значения в целое.

Несмотря на то, что современный C теперь имеет boolтип, он все еще сохраняет достоверность кода, подобного этому, и потому, что это хорошо, и из-за огромного сбоя с обратной совместимостью, который мог бы возникнуть в противном случае.

Еще один пример, где C ловко: тестирование двух логических условий в качестве четырехходового переключателя:

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

Вы не могли бы отнять это у программиста C без боя!

Наконец, C иногда служит своего рода языком ассемблера высокого уровня. В языках ассемблера у нас также нет логических типов. Логическое значение - это всего лишь бит или ноль в сравнении с ненулевым значением в ячейке памяти или регистре. Целый ноль, логический ноль и нулевой адрес проверяются одинаково в наборах команд на ассемблере (и, возможно, даже с плавающей точкой ноль). Сходство между C и ассемблером полезно, например, когда C используется в качестве целевого языка для компиляции другого языка (даже того, который имеет строго типизированные логические значения!)

Kaz
источник
0

Логическое или истинное значение имеет только 2 значения. Правда и ложь.

Они должны не быть представлены в виде целых чисел, но в качестве битов (0 и 1).

Сказать, что любое другое целое число, кроме 0 или 1, не является ложным, является ошибочным утверждением. Таблицы истинности имеют дело со значениями истинности, а не целыми числами.

С точки зрения истинного значения -1 или 2 сломают все таблицы истинности и любую логическую логику, связанную с ними.

  • 0 И -1 ==?!
  • 0 ИЛИ 2 ==?!

Большинство языков обычно имеют booleanтип, который при приведении к числовому типу, такому как целое число, показывает ложь, которая будет приведена к целочисленному значению 0.

Джон Рейнор
источник
1
0 AND -1 == к какому бы логическому значению вы их ни приписывали. Это то, что мой вопрос о том , почему литье их TRUEили FALSE. Никогда я не говорил - может быть, я и сделал, но это не было предназначено - целые числа были истинными или ложными, я спросил о том, почему они оценивают, в зависимости от того, что приведено к логическому значению.
Morwenn
-6

В конечном счете, вы говорите о взломе основного языка, потому что некоторые API являются дерьмовыми. Crappy API не новы, и вы не можете исправить их, нарушив язык. Это математический факт, что 0 ложно, а 1 верно, и любой язык, который не уважает это, в корне нарушен. Трехстороннее сравнение является нишевым и не имеет никакого смысла, поскольку его результат неявно конвертируется, boolпоскольку он возвращает три возможных результата. Старые API-интерфейсы C просто ужасно обрабатывают ошибки, а также имеют проблемы, потому что C не имеет необходимых языковых возможностей, чтобы не иметь ужасных интерфейсов.

Обратите внимание, что я не говорю, что для языков, которые не имеют неявного целочисленного -> логического преобразования.

DeadMG
источник
11
«Это математический факт, что 0 ложно, а 1 верно».
Р. Мартиньо Фернандес
11
Можете ли вы привести ссылку на ваш «математический факт, что 0 ложно, а 1 истинно»? Ваш ответ звучит опасно, как напыщенная речь.
Дэн Пичельман
14
Это не математический факт, но это было математическое соглашение с 19-го века.
Чарльз И. Грант
1
Булева алгебра представлена ​​конечным полем, в котором 0 и 1 являются единичными элементами для операций, которые напоминают сложение и умножение. Эти операции, соответственно, ИЛИ и И. Фактически, булева алгебра написана во многом как нормальная алгебра, где сопоставление обозначает И, а +символ обозначает ИЛИ. Так, например, abc + a'b'cозначает (a and b and c) or (a and (not b) and (not c)).
Каз