109

It seems that the use of the exclamation mark ! to denote negation started with the C programming language (as far as I can tell from my Google research). Nowhere though is mentioned who and why chose this symbol specifically, other than that it was simply available.

So, the question is: when, by whom, and why was ! chosen?

user3840170
  • 23,072
  • 4
  • 91
  • 150
blues
  • 1,099
  • 2
  • 5
  • 7
  • 14
    Well, this usage was not inherited from BCPL, which used ! as the array indexing operator. (The Development of the C Language) – dan04 Jun 14 '21 at 18:16
  • 9
    Doesn't look like it's from APL, my "goto" language when it comes to wacky characters :! is the factorial function. – ErikF Jun 14 '21 at 19:01
  • 3
    @dan04 it seems to have arrived between BCPL and B then, per A Tutorial Introduction to the Language B by Kernighan. – Tommy Jun 14 '21 at 20:16
  • 32
    Probably because != is as close to ≠ as you can render in a limited character set, but it is only an opinion, not an answer. – Brian Tompsett - 汤莱恩 Jun 14 '21 at 20:46
  • 18
    @BrianTompsett-汤莱恩: /= or |= would also work as approximations of , but C uses those operators for other things. – dan04 Jun 14 '21 at 21:56
  • 3
    @dan04 But weren't the early augmented assignment operators like =+, =/? – texdr.aft Jun 15 '21 at 02:44
  • 18
    The shame here is that UK keyboards have a key dedicated to the formal symbol for Logical Negation ¬ (and the broken-pipe symbol) - but the US keyboard doesn't - so methinks if K&R had a British keyboard for some reason then we'd be using better symbology all around, especially in C-derived languages today :) – Dai Jun 15 '21 at 04:49
  • 2
    @Dai that's probably the most pointless symbol on the UK keyboard, along with the duplicate pipe on the same key – Chris H Jun 15 '21 at 11:30
  • 2
    @ChrisH: Is it actually a duplicate, or different keys for | and ¦? – dan04 Jun 15 '21 at 15:29
  • @dan04 I nearly submitted the comment calling one "pipe" and one "vertical bar", then noticed the glyphs were identical on the screen. So I tested: in a terminal they look identical and both work for bash pipelines. Visually they match | U+007C vertical line and not ¦ U+00A6 broken bar. But this may be a glitch in Ubuntu's keyboard layout. I ought to remap one to something more useful (and ¬ for that matter) – Chris H Jun 15 '21 at 15:34
  • @Dai - UK keyboards having ¬; not in my experience they didn't. My experience in the mid-70s was Flexowriters and Teletypes, and then imported DEC gear. In fact, I ran into no character set with ¬ in it, except for IBM gear. Maybe you had an ICL keyboard after the 2900 adopted EBCDIC? Can you elucidate? – dave Jun 15 '21 at 23:09
  • @another-dave The key in the top-left corner: https://en.wikipedia.org/wiki/File:KB_United_Kingdom_Ext.png - The official standard is BS 4822, however it was withdrawn in 2014 apparently - with no replacement. I guess that means the BSI has stopped setting a spec for UK keyboard layouts then - which is odd. UPDATE: Oh, now it's BS/ISO/IEC 9995-9 updated in 2016: https://shop.bsigroup.com/ProductDetail?pid=000000000030378348 – Dai Jun 16 '21 at 10:42
  • @dai - Thanks. BS 4822 was published 1994, replacing a 1980 version. But I think no 'not' sign when C was being invented. The 1980 spec mentions the 7-bit ISO code, which AFAIK had no 'not'. Still, this is all might-have-been. – dave Jun 16 '21 at 12:03
  • 1
    @BrianTompsett-汤莱恩 The 'xBase' languages use # as the not-equal operator; I guess the thought is that 2 slashes through the equals are better than one. – LAK Jun 16 '21 at 14:18
  • 3
    The negation symbol ¬ was fairly widely used in published papers in 1969. But it wasn't in 7 bit ASCII. And that's probably the target character set for B, C , and Unix v0.0 – Walter Mitty Jun 16 '21 at 18:41
  • is a better approximation of ≠, and was used as such in most BASICs descended from HP. This was definitely not available on UK keyboards though, but given it was Bell I doubt this was the reason. – Maury Markowitz Jun 16 '21 at 19:18
  • @dan04 vertical bar (both with hole and without) history even in ASCII (which is far from single exact standard, but rather myriad of partly incompatible revisions) is riddled with incompatible changes, see https://jkorpela.fi/latin1/ascii-hist.html for example – Matija Nalis Jun 16 '21 at 20:13
  • 1
    It occurs to me that, in a language that did not confuse boolean and integer types, - would have worked for logical negation. Did any language do that? – dave Jun 17 '21 at 02:48
  • @ChrisH • In earlier ASCII, the ¦ at 0x7C (broken bar) was to distinguish it from | (vertical line) as the alternative glyph at 0x21 (normally ! (exclamation mark)), which was a concessions to 6-bit character set computers. Later ASCII changed that glyph assignment. Earlier IBM PC computers still had ¦ in its extended ASCII character set. – Eljay Jun 15 '22 at 14:15

7 Answers7

109

Since the few document trails on this topic quickly run cold, I contacted Ken Thompson. He confirmed that if there was anything he would have been influenced by at the time, it would have been BCPL and SMALGOL. But given that these don't use ! for negation, he "might have made it up".

I don't suspect there is anything more to it that can be researched.

Kaz
  • 1,660
  • 1
  • 7
  • 14
  • 32
    If I may ask, how does one contact Ken Thompson? – parktomatomi Jun 16 '21 at 13:27
  • 13
    If Kaz is a Googler then it should have been pretty easy; when I last checked both Thompson and Kernighan were on the payroll, so they'll have appropriate internal corporate email addresses. – Tommy Jun 16 '21 at 18:44
  • 8
    @Tommy: he isn't. When going from his profile, you quickly end up at the LinkedIn, CV etc. with his current employer. Ken's email address can easily be found with a quick duckduckgo search. – AnoE Jun 17 '21 at 10:57
57

The ! was around as part of the B programming language, according to the "User's Reference For B" (K Thompson, Jan 1972).

Somewhere between BCPL and B, the decision was made to use !.

In the Reference above, I can't find an indication of why it was chosen. "The NOT prefix unary operator ! takes an integer value operand. The result is zero if the operand is non-zero. The result is one if the operand is zero."

Leo B.
  • 19,082
  • 5
  • 49
  • 141
Clinton Pierce
  • 791
  • 3
  • 3
  • That's an earlier reference than the one I just posted as a comment, but seems to survive at archive.org should anybody else want to check it out. – Tommy Jun 14 '21 at 20:18
  • 1 => 0, 0 => 1... that seems like negation... – no ai please Jun 15 '21 at 05:24
  • 13
    @Someone_who_likes_SE: it's logical negation, so ! belongs to the same group as || and && logical operators, which evaluate numeric operands according to != 0 being true. C's other negation operators are arithmetic - (value negation, subtract-from-0), and ~ (bitwise inverse, one's complement negation). Fun fact: on a 1's complement machine, unary - and ~ are the same thing, I think. – Peter Cordes Jun 15 '21 at 09:12
  • 2
    @ilkkachu: I was wondering whether there might be any difference in their semantics in C, not just the binary operation. e.g. whether -0 would create a 0xffffffffU bit-pattern like ~0 would (which may be a trap representation on some 1's complement machines, so you'd want to implement signed int -x in a way that did 1's complement subtraction from 0, not just flipped the bits, on such a machine). I also wondered about signed-overflow UB, but now that I think about it, only 2's complement has a number whose negation isn't representable in the same type. – Peter Cordes Jun 15 '21 at 10:49
  • 3
    The PDP-1 was a ones complement machine, and yes negating a number gave the same answer as flipping all the bits. Except for zero. If you negate it, this gives all ones, which is minus zero. But the PDP-1 had some circuitry to convert minus zero to zero. I forget whether it activated in this case. – Walter Mitty Jun 15 '21 at 11:28
  • 2
    Is it just a coincidence that ~ and ! are next to each other on the keyboard? – Walter Mitty Jun 15 '21 at 11:38
  • @PeterCordes, ah, right. and it does make sense that some arithmetic would avoid negative zero, while bit flip obviously shouldn't... – ilkkachu Jun 15 '21 at 12:21
  • @WalterMitty: I don't know of any commonplace keyboard layouts that were in use before the 101-key PC keyboard that placed the ~ key next to the 1. – supercat Jun 15 '21 at 19:04
  • 8
    @WalterMitty The various UNIX conventions are generally built around the keyboard layout of the ADM-3A terminal. (eg. ~ and HOME share a key, as do HJKL and the arrows keys.) – ssokolow Jun 16 '21 at 01:31
  • @PeterCordes your "fun fact" about 1's complement is probably true, but good luck finding a machine to test it on. – Mark Ransom Jun 16 '21 at 03:27
  • Where was the squiggle on an ASR-33 keyboard? That teletype might have been the console for the PDP-7 where unix was developed. – Walter Mitty Jun 16 '21 at 09:25
  • Based on https://en.wikipedia.org/wiki/Vertical_bar#Solid_vertical_bar_vs_broken_bar and https://www.youtube.com/watch?v=BktIY7VbrUs my assumption is (couldn't find sources) that ! was chosen in order to avoid confusion of competing character encoding standards wrt. ¦ (proposed programmers' ! in some standards) and ¬ (proposed programmers' ^). – D. Kovács Jun 16 '21 at 13:22
  • @WalterMitty: Teletype 33 (and 35) did not have chars 0x60-0x7e (in modern notation) including squiggle; this also omits lowercase, making them just about unusable for C or more generally Unix -- although the Labs tty driver had a hack to allow you to 'downshift' letters. I don't recall ever testing if it worked for uparrow (or hat) to squiggle. OTOH 37 was just available, and anyway people then wouldn't use the console for dev work, reserving it for logging and boot and (single-user) maintenance tasks. – dave_thompson_085 Jun 28 '21 at 10:50
31

Likely because it's wasn't a widely used mathematical operator, and wasn't a quote symbol.

Other answers have pointed out that ! was used in B and then found its way into C.

The popular "not equals" operators in computer languages are:

  • != → Probably meant to look like . ! means factorial, but it's not an arithmetic operator. Most popular languages don't have a factorial operator, and on most non-trivial factorial operations like combinations and permutations there are algorithms that let you not do the full factorial math to solve them.

  • <>< and > both have separate meanings in most languages, and < and > imply an order (i.e. something is greater than something else), which true and false don't really have.

  • =/= → The / already means divide, and /= means divide and save the results. It couldn't be used as a single character anyway, and a minor typo completely changes what it does.

  • '" (and back-ticks `) → The "not" symbol in math is usually the character written after the variable (so A'). It also indicates strings or characters, so it would have been confusing and likely difficult to parse. Back-ticks ` and ' look extremely similar so reading it would have been difficult. It's also pretty surprising how many programmers do not know what a back-tick is.

So out of all the ASCII symbols, ! is the one least likely to cause typos and confusion. It's on the wrong side of the variable to mean factorial, and even if someone was confused, a single run of the program would have shown them the error.

Some other choices and why they probably weren't in the running:

  • | - Already used to mean OR. Also, the pipe command, which was already used in Unix/Linux as a way to chain commands together, so probably not a good choice. People might have tried to chain functions together

  • % - Means modulo and percentage. Already widely used for something else.

  • : - Already used by labels and the ?: conditional shortcut. Looks really similar to ; which ended statements in C. Also was already used by Smalltalk and C++ for inheritance.

  • # - Part of preprocessor directives in C and used by many shells.

  • ^ - Sometimes means "to the power of" in languages, so already taken.

  • ? - Already a short-cut for if in many languages, and would imply you're asking a question instead of negation.

  • $ - Means money. Also heavily used by many shells to mean variable.

  • & - Means "and". Already used as a bitwise or logical AND in many languages.

  • ~ - Not widely used in most programming languages, but ~= means "approximately equal to" in mathematical text. So probably would have added more confusion.

  • @ - Means "at". Not widely used in programming languages, but widely used in emails, which were invented in 1978. Not a good choice because it's an abbreviation for a word.


The comments are awesome. At first trying to pull some into the answer, but too many, and too many great history points.

Toby Speight
  • 1,611
  • 14
  • 31
sevensevens
  • 451
  • 3
  • 4
  • 3
    Also very few widely-used languages have a native factorial operator - few that I can think of even have a standard library function for it - I'm not sure how useful such a thing would be (outside of very specifically math-based languages, or esoteric code-golf languages that have built-ins for everything). Also, every keyboard layout since way back has a ! key, so it's easy to type - compare to APL symbols for which you need a specialized keyboard to use efficiently... – Darrel Hoffman Jun 15 '21 at 19:04
  • 1
    Re using ' for "not," MUMPS (which uses ! for "or") does this, and also allows you to prefix any operator that returns a boolean value, thus from > "greater than," ! "or," and [ "contains substring" can be derived '> "not greater than," '! "nor," and '[ "does not contain substring." – A. R. Jun 15 '21 at 19:23
  • 1
    @DarrelHoffman APL, which you mention, is from 1966, and in it !Y denotes the factorial of Y. And X!Y denotes the binomial coefficient "Y over X". Logical negation is ~Y there. I think it was at about that time that ~ was included in ASCII (while ! was there from 1963). Of course APL had and has many symbols which were not included in ASCII. – Jeppe Stig Nielsen Jun 15 '21 at 19:24
  • 19
    <> was used as not equal in several BASIC dialects. – UncleBod Jun 15 '21 at 20:09
  • 7
    Neglected to mention it above; C does use : for the ternary operator (along with ?). Not sure when it dates from in C though (the ternary operator originated with CPL ca. 1963). – Alex Hajnal Jun 15 '21 at 21:43
  • 4
    Of course, +-*/ already had well-established usage as arithmetic operators, <> for relationals, . as the decimal point, , for separating function arguments, and = for assignment. '" are used for quoting characters and strings. ()[]{} look weird if not used in pairs. Your answer rules out |%:;#^?. _ is ruled out by being allowed to start identifiers. \ is used for line continuation. & more intuitively represents an AND operation, rather than a NOT. That leaves !$@``~ as the only ASCII punctuation left. – dan04 Jun 16 '21 at 00:12
  • 12
  • 6
    Regarding ' it is maybe worth noting that ! was originally ' overstruck with . – Adám Jun 16 '21 at 07:57
  • 2
    : is used for goto labels and case statements in C. – cup Jun 16 '21 at 12:46
  • 3
    is the non-equal operator in xBase languages (dBase and its descendants). – LAK Jun 16 '21 at 14:20
  • 3
    @RussellBorogove Similarly the @ in email ('71) and the : in Smalltalk ('72) and C++ ('85). And some of the other examples, e.g. =/= wouldn't cause ambiguities in a lookahead (1) parser, since while there are multiple meanings of =, if you see a = followed by a /, there's only one legal parse that can follow from that. – Ray Jun 16 '21 at 16:10
  • 7
    Regarding ~: The C bitwise operators were assigned before the logical operators. ~ was assigned to bitwise negation, being somewhat similar to the mathematical ¬ symbol. Therefore, ~ was unavailable later when the logical operators were created. – DrSheldon Jun 16 '21 at 16:34
  • 3
    "~= means approximately equal to in mathematic text. So probably would have added more confusion." MATLAB uses ~ for not and ~= for not equal. And MATLAB was designed by a mathematician. So I don't think this argument holds. @DrSheldon's argument seems more solid. – Cris Luengo Jun 16 '21 at 23:18
  • 1
    You forgot to mention the not-equals sign used in Algol (60 and 68) -- (at least in the reference language). On KDF9 Algol prepared on a Flexowriter, that's equals overstruck with slash, but it's still a single 8-bit character on the disk. – dave Jun 17 '21 at 00:29
  • 2
    Concerning being on the "wrong side of the variable to mean factorial" I'd like to point out that!n is the notation used for derangements in mathematics: https://en.wikipedia.org/wiki/Derangement – phk Jun 17 '21 at 06:04
  • 3
    Since the bitwise operators & and | led to the logical operators && and ||, I could similarly see ~~ being the logical not operator, though that does already have a meaning as a double bitwise negation, if a somewhat useless one. Also there's no logical XOR (^^) for some reason. – user3570736 Jun 17 '21 at 10:23
  • 4
    "already used in Unix/Linux" — Linux would only appear decades after C syntax had long stabilized. – Ruslan Jun 17 '21 at 11:40
  • 2
    REXX uses the ¬ operator, or \`` on machines where it isn't available, whilst the ARexx dialect uses~` to the same effect. This answer is ahistorical and very far from a survey of what was established when C was first made. – idrougge Jun 17 '21 at 15:42
  • 1
    As for !=, maybe they simply chose it because they liked it better than <>. As for @, it is not "an abbreviation for a word" but is a graphic representation of the word at, which was very easy to write by hand. Modern typefaces have made it very stylized, as happened similarly to & over centuries from its origin et. – Suncat2000 Jun 17 '21 at 15:54
  • 1
    Note that * / + - ^ & % | are binary operators, so that = /= += -= ^= &= %= |= have to have the modifying-assignment meanings of "a = b" and so on. This is a syntactic reason why the unary operator symbol ! can be reused in !=, which is not a modifying assignment. This adds to the semantic reason of having both ! and != involve negation. – Paul Boersma Jul 05 '21 at 20:42
  • 4
    This list looks impressive, until you realise it's absolutely stuffed with anachronisms and non sequiturs. The most blatantly nonsensical is that the suggestion that C didn't use something because it was "already used by C++". – IMSoP Jul 12 '21 at 17:49
20

When did ! as NOT surface?

BCPL defined a number of logical operators, but these were all bitwise logical operators:

4.5 Logical Operators

[paraphrased: bitwise logical]

      ~ E1 (also not E1)
or E1 & E2
or E1 \ E2

Some bitwise operators survived into B, particularly its AND (&) and OR (|) operators, whereas bitwise NOT did not. B introduced, however, the NOT unary prefix operator that was not a bitwise operator, but the integer logical NOT as we know it in C today:

4.2 Unary Operators

  1. The NOT prefix unary operator ! takes an integer rvalue operand. The result is zero if the operand is non-zero. The result is one if the operand is zero.

As this was a new operator not present in its predecessor BCPL, B had to make a decision as for what syntax to use for the operator. As this was moreover not to be confused with the bitwise NOT of BCPL, one might speculate that re-using ~ was not considered a good way forward. As for why ! was chosen given the (speculative) rejection of ~, another answer presents some good (albeit speculative) ideas.


What came first, != or !?

Finally, note that B also introduced the equality operators == (equal to) and != (not equal to), also not present in its predecessor, and it’s arguably

  • easier to see how != originated given it’s shape as compared to the non-ANSII symbol for non-equality, and
  • if so, how != as “not equal to” made breaking out ! as the logical “not” was a somewhat natural choice.
dfrib
  • 301
  • 1
  • 4
8

I always assumed we stole OR and NOT from propositional logic. It's roughly 15 years older than programming languages, and writes things like p|q and p & !q. Sure, they prefer the small 2-line right-then-down for a "not", but you can't type that; likewise the almost-V and upside-down V for OR and AND.

Owen Reynolds
  • 339
  • 1
  • 5
  • 4
    In fact, the whole reason that backslash made it into ASCII in the first place was to allow programming languages to use / and /\ as operators. – dan04 Jun 16 '21 at 00:16
  • 6
    Could you add some source for the usage of ! that predates the programming usage? – blues Jun 16 '21 at 06:51
  • 9
    Propositional logic is just 15 years older than programming languages??? – Kaz Jun 16 '21 at 07:51
  • 2
    I'd argue propositional logic is a bit older than that: https://en.wikipedia.org/wiki/Chrysippus For modern style first-order predicate logic, we are still in the 19th century, with Frege and others – leo Jun 16 '21 at 08:23
  • 5
    “Big if true.” If “!” was was used in propositional logic before C, then this seems a very likely explanation. But the prefix symbols I can remember seeing for negation in earlier logic literature are ¬, –, and ~. This answer would be great if it could be expanded with a source citation. (And it’d be equally interesting to see a source for @dan04’s comment!) – Peter LeFanu Lumsdaine Jun 16 '21 at 21:59
  • @blues I'm not enough of a researcher. I'm 90% sure I remember seeing a well-worn prop-logic textbook in the mid-80's using them, but that's it. – Owen Reynolds Jun 16 '21 at 22:06
  • 3
    I recall we used to write propositions with . (or nothing) and +. AB+C is a lot shorter than A ∧ B ∨ C (though we knew that notation too) and the rules are the same as algebra. Negation (to the point of the current question) was an overbar in either notation. – dave Jun 17 '21 at 01:33
  • 1
    @another-dave Sure, a bar over the letter, but you also couldn't easily type that in 1980. So along with 3 ways to negate, !p being a 4th isn't so unlikely. But then even if ! was used, maybe not widely enough to be stolen. I feel much more confident vertical bar was used for p or q. – Owen Reynolds Jun 17 '21 at 02:30
  • @OwenReynolds I was using "pen and paper" at the time :-) – dave Jun 17 '21 at 02:40
1

I don't know if this has the logical ring to it that many in the computing community appreciate, but I can't get past an emotional interpretation:

"DON'T do that!" always has a "!" on the end of it. At least, it certainly did since childhood for me. Other examples: "No!" and "I told you, NO!".

There's a pretty strong negation in that character. It symbolizes alarm, that there's something wrong. I'd choose that character for negation.

Even now, whenever I see an exclamation mark, there is a sudden amygdalic spasm and a subconscious urge to pull back my hands and cover my mouth while I stare I what I shouldn't have been doing.

So where in a positive case, it might be == "yes, this", its != "no, NOT this!". i.e. Stop. ALARM Stop what? Stop 'this'.

Before I encountered coding (which isn't very much, btw) or logic (perhaps even less), I used to use "!" in handwritten notes for lab protocols ("caution", "error", etc, (but also, "activated") or as a shorthand for pain/physical injury in my training diaries.

1

I recall in my pre-computer days throughout my college math major courses (1968-1970), we were using the ! symbol in proofs as negative (meaning not). It appears that the use of the symbol "!" as "NOT" was not first begun with the use of the programming language C (which was one of several programming languages I later used), but was likely brought into C because of its use in logic and mathematic proofs. If we are looking for why it was used in C, that appears to provide a rationale. My school notes and textbooks are long gone, but there are some online links that support my memory: (1) https://stat.ethz.ch/R-manual/R-devel/library/base/html/Logic.html (2) https://en.m.wikipedia.org/wiki/List_of_logic_symbols

DPaden50
  • 19
  • 2
  • Maybe so, but this doesn't answer the question of why. – Chenmunka Jul 02 '21 at 09:02
  • If you have a reference to a textbook or any other material that was used in those courses that has the usage of !, and also predates the programming usage this could be a great answer. – blues Jul 02 '21 at 09:05
  • 2
    You missed the most important part: ‘and also predates the programming usage’. Neither Wikipedia nor the R manual meet this criterion. – user3840170 Jul 04 '21 at 09:44