<p>Are there benefits to number literal tokens including negative numbers? Or is the string <code>-1234</code> being parsed as unary negation of positive number literal <code>1234</code> more beneficial?</p>

<language-design><tokens><literals>
Negative literals, or unary negated positive literals?
CC BY-SA 4.0
2023-07-10T21:47:15.183Z
I would go for negative literals, for the reasons specified in the answer.
CPlus
CC BY-SA 4.0
2023-07-14T04:47:18.903Z
@bigyihsuan, Looks like pros and cons either way.  What ever choice is made, some folks are unhappy.
chux
<h2>Exponentiation doesn't like negative literals</h2>
<p>Languages with an exponentiation operator prefer to parse negative numbers as unary negation, because exponentiation has higher precedence than unary negation: <code>-3^2</code> should evaluate to <code>-9</code>, not to <code>+9</code>.</p>

CC BY-SA 4.0
2023-07-14T02:00:17.900Z
2023-08-04T12:40:27.320Z
2023-08-04T12:40:27.320Z
hugomg
<p>If <code>-</code> is a binary operator in your language, then you probably shouldn't make <code>-1</code> a single token. If you do, then <code>x-1</code> will be lexed as two tokens ─ an identifier <code>x</code> followed by a literal <code>-1</code> ─ and then you can't parse it as a binary operator expression like <code>x - 1</code>, unless your parser has a weird special case for treating negative number literals like postfix operators.</p>
<p>Yes, you also need a special case if you want to reject out-of-bounds integer literals as syntax errors while accepting <code>INT_MIN</code>, whose absolute value is out-of-bounds. But that's a considerably easier special case. For example, <a href="https://docs.oracle.com/javase/specs/jls/se20/html/jls-3.html#jls-3.10.1" rel="nofollow noreferrer">here's how the Java Language Specification handles it</a>:</p>
<blockquote>
<p>The largest decimal literal of type <code>int</code> is <code>2147483648</code> (2<sup>31</sup>).</p>
<p>All decimal literals from <code>0</code> to <code>2147483647</code> may appear anywhere an int literal may appear. The decimal literal <code>2147483648</code> may appear only as the operand of the unary minus operator <code>-</code> (§15.15.4).</p>
<p>It is a compile-time error if the decimal literal <code>2147483648</code> appears anywhere other than as the operand of the unary minus operator; or if a decimal literal of type <code>int</code> is larger than <code>2147483648</code> (2<sup>31</sup>).</p>
</blockquote>
<p>There is a similar rule for decimal literals of type <code>long</code>.</p>

CC BY-SA 4.0
2023-08-04T13:58:25.713Z
2023-08-04T13:58:25.713Z
kaya3
<p>The advantage of “unary negated positive literals” means avoiding a special case in your parser.  You can treat the <code>-</code> in <code>-1234</code> the same way that you treat the <code>-</code> in <code>-x</code> or <code>-(x * y + z)</code>.  It is for this reason that Jack Crenshaw's <a href="https://compilers.iecc.com/crenshaw/" rel="noreferrer">Let's Build a Compiler</a> chose that approach (as well as rewriting unary <code>-x</code> to binary <code>0-x</code>).</p>
<p>It does theoretically make the generated machine code slightly less efficient, but a simple <a href="https://en.wikipedia.org/wiki/Constant_folding" rel="noreferrer">constant folding</a> optimization will turn the expression into the desired negative constant.</p>

CC BY-SA 4.0
2023-07-10T21:57:01.997Z
2023-07-10T21:57:01.997Z
dan04
<h2>Referential transparency</h2>
<p>This is probably the big one. If you have a negation operator rather than negative literals, you can replace <code>-5</code> with <code>-a</code> (where <code>a</code> is an integer variable) everywhere, and nothing else about the program will change.</p>
<p>This isn't true if <code>-5</code> is a token.</p>
<p>However...</p>
<h2>Interaction with other syntax</h2>
<p>To quote from <a href="https://www.haskell.org/onlinereport/haskell2010/haskellch10.html#x17-18100010.6" rel="noreferrer">the Haskell 2010 report, section 10.6</a>:</p>
<blockquote>
<p>The handling of the prefix negation operator, <code>-</code>, complicates matters
only slightly. Recall that prefix negation has the same fixity as
infix negation: left-associative with precedence 6. The operator to
the left of <code>-</code>, if there is one, must have precedence lower than 6
for the expression to be legal. The negation operator itself may
left-associate with operators of the same fixity (e.g. <code>+</code>). So for
example <code>-a + b</code> is legal and resolves as <code>(-a) + b</code>, but <code>a + -b</code> is
illegal.</p>
</blockquote>
<p>The negation operator also interacts with function application in ways that may be non-obvious to those coming from other languages; <code>f -1</code> doesn't mean <code>f (-1)</code>, it means <code>f - 1</code>.</p>
<p>To complicate things even further, Haskell doesn't have a unary plus operator, so  <code>f (-1)</code> means negative one passed to the function <code>f</code>, but <code>f (+1)</code> means the operator section <code>(\x -&gt; x + 1)</code> passed to <code>f</code>.</p>
<p>This is not unique to Haskell, but it's made more obvious because of the (otherwise extremely convenient) function call and operator section syntax. Some variants of Prolog have related issues. Now that I think of it, I doubt there are two Prolog implementations which parse the same way.</p>
<p>For comparison, C has 13 levels of operator precedence and many operators which can be both prefix and infix. Most C programmers never seem to find themselves needing to put parentheses around a negative integer, but the same can't be said of the prefix <code>*</code> operator.</p>

CC BY-SA 4.0
2023-07-11T01:13:06.563Z
2023-07-11T01:13:06.563Z
Pseudonym
CC BY-SA 4.0
2023-07-11T10:58:15.350Z
Incidentally, GHC has [a syntactic extension](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/lexical_negation.html#extension-LexicalNegation) that avoids the complications you mention (allowing to write e.g. `9 + -8` and `map (- 2) [10..20]` and `abs -pi`). Basically, what it does is to treat `-` as unary negation iff it has an expression to its right without whitespace in between, but not an expression to its left without whitespace in between. Else, it's treated as infix minus operator. ...
leftaroundabout
CC BY-SA 4.0
2023-07-11T11:03:48.050Z
...And if the expression to the right of the unary minus `-` is a number literal, the whole thing is syntactically treated as a single negative-valued literal, else it becomes a call to `negate`.
leftaroundabout
<h2><code>INT_MIN</code></h2>
<p>In C, negative literals are processed as unary negation on a literal. So, <code>INT_MIN</code> must be defined as <code>-2147483647 - 1</code> rather than <code>-2147483648</code>.</p>
<h2><code>-0</code></h2>
<p>In Swift, negative literals can be a single token. However, this means <code>-0 as Double</code> and <code>-0.0</code> are different. <code>-0.0</code> and <code>-(0 as Double)</code>, however, are the same.</p>
<p>However, Swift isn't even consistent about this. When method calls or postfix operators are involved, they bind more tightly than the unary minus, so <code>-0.foo()</code> is parsed as <code>-(0.foo())</code>. As I mentioned <a href="https://langdev.stackexchange.com/questions/2276/negative-literals-or-unary-negated-positive-literals/2277#comment4085_2280">in a comment</a>, this means that in some cases, <code>-0.foo()</code>, <code>(-0).foo()</code>, and <code>(-(0)).foo()</code> are all different.</p>

CC BY-SA 4.0
2023-07-10T21:36:03.047Z
2023-07-13T20:21:30.567Z
2023-07-13T20:21:30.567Z
Bbrk24
CC BY-SA 4.0
2023-07-11T07:00:42.113Z
+1 for the INT_MIN edge case, as `2147483648` is not a valid 32-bit signed integer, but `-2147483648` should be. Whereas the second part is more about the (poor?) IEEE choice to make `0.0` and `-0.0` two different values.
Ralf Kleberhoff
CC BY-SA 4.0
2023-07-12T04:08:00.033Z
Signed zero does have some purpose -- if you take a negative value and divide it by a sufficiently large value to completely reduce its mantissa to 0 (due to limited bit precision), you have not lost the fact that it was originally negative.  This is occasionally important.
Miral
CC BY-SA 4.0
2023-07-12T05:09:54.930Z
Seems like you can avoid the former issue by just allowing the compiler to read (say) bigints, and then have logic that deals with the case where the literal describes a number too large to fit into whatever you're trying to store it in.
Daniel McLaury
CC BY-SA 4.0
2023-07-12T16:08:45.200Z
This `-0` can easily be linted against, though. Nothing simpler than syntactic linters, after all.
Matthieu M.
CC BY-SA 4.0
2023-07-15T15:22:12.397Z
The `INT_MIN` issue may or may not be a problem for all languages, of course.  For example, part of my current language design is to have literals and expressions involving them default to an infinite-precision type until they interact with something that has a defined precision, e.g. by being assigned to a variable or used in an expression alongside something that can't be computed at compile time.  In such a language, this is no issue at all: `const INT_MIN = -(2147483648)` works just fine, but just needs a little more memory to compile.  OTOH, I do have negative literals....
occipita
CC BY-SA 4.0
2023-08-04T16:38:25.923Z
@occipita: I wonder why I haven't seem more languages adopt such an approach (though perhaps limiting the maximum precision to limit the amount of memory required for something like `(7 << 1000000) + 5 - (1 << 1000003)) + (1 << 1000000)`)?  Treating literals as their own type would seem to have a variety of advantages.
supercat
<h3>Do both (avoid abiguity or parenthesis)</h3>
<p>The overloading of <code>-</code> for negative literals and negation causes ambiguity:<sup><a href="https://en.wikipedia.org/wiki/Plus_and_minus_signs#Uses_in_computing" rel="noreferrer">W</a></sup></p>
<blockquote>
<p>In mathematics and most programming languages, the rules for the order of operations mean that −5<sup>2</sup> is equal to −25: Exponentiation binds more strongly than the unary minus, which binds more strongly than multiplication or division. However, in some programming languages (Microsoft Excel in particular), unary operators bind strongest, so in those cases <code>−5^2</code> is 25, but <code>0−5^2</code> is −25.</p>
</blockquote>
<p>To avoid this (and to allow adjacent numerical tokens not causing subtraction)  a language can use a separate character for negative literals. APL uses <code>¯</code> while J uses <code>_</code>.<sup><a href="https://en.wikipedia.org/wiki/Plus_and_minus_signs#Uses_in_computing" rel="noreferrer">W</a></sup></p>

CC BY-SA 4.0
2023-07-10T22:21:54.513Z
2023-07-10T22:21:54.513Z
Adám
CC BY-SA 4.0
2023-07-11T08:27:17.153Z
one of those times when the solution is worse than the problem!
Criticize SE actions means ban
CC BY-SA 4.0
2023-07-11T11:53:41.630Z
off, but why would a programming language select an uncommon unicode token that's not present in usual keyboard layouts for such a commonly used sign as the unary minus? Seems to be a nightmare to type it.
Neinstein
CC BY-SA 4.0
2023-07-11T13:10:05.340Z
@Neinstein APL is designed to use a custom keyboard/font _anyway_. See [this image search](https://www.google.com/search?q=apl+keyboard&tbm=isch) for examples.
KRyan
CC BY-SA 4.0
2023-07-13T15:29:21.937Z
See also in Javascript: `-64.0.toString(16)` evaluates to the number -40 vs `(-64.0).toString(16)` evaluates to the string '-40'
corvus_192
CC BY-SA 4.0
2023-07-13T20:05:39.427Z
@corvus_192 I'll do you one worse: in Swift, it's possible to devise a method `foo` for which `-0.foo()` , `(-0).foo()`, and `(-(0)).foo()` are all different. [Proof by example](https://swiftfiddle.com/3wyrh43uuzg6vflufcf762k3am)
Bbrk24
CC BY-SA 4.0
2023-08-04T13:16:49.957Z
You could also avoid that ambiguity by making your precedence into a DAG requiring parenthesisation, instead of a list.
Longinus
# Negative literals, or unary negated positive literals?
Posted by: username_0 on 2023-07-10
Are there benefits to number literal tokens including negative numbers? Or is the string `-1234` being parsed as unary negation of positive number literal `1234` more beneficial?
**Tags:** language-design tokens literals
> **Comments:**
> **username_1**: I would go for negative literals, for the reasons specified in the answer.  
> **username_2**: @bigyihsuan, Looks like pros and cons either way.  What ever choice is made, some folks are unhappy.  
---
## 6 answers
### Answer by username_8 on 2023-07-10. Score: 31
## `INT_MIN`

In C, negative literals are processed as unary negation on a literal. So, `INT_MIN` must be defined as `-2147483647 - 1` rather than `-2147483648`.

## `-0`

In Swift, negative literals can be a single token. However, this means `-0 as Double` and `-0.0` are different. `-0.0` and `-(0 as Double)`, however, are the same.

However, Swift isn't even consistent about this. When method calls or postfix operators are involved, they bind more tightly than the unary minus, so `-0.foo()` is parsed as `-(0.foo())`. As I mentioned [in a comment](https://langdev.stackexchange.com/questions/2276/negative-literals-or-unary-negated-positive-literals/2277#comment4085_2280), this means that in some cases, `-0.foo()`, `(-0).foo()`, and `(-(0)).foo()` are all different.
> **Comments:**
> **username_9** (2023-07-11): +1 for the INT_MIN edge case, as `2147483648` is not a valid 32-bit signed integer, but `-2147483648` should be. Whereas the second part is more about the (poor?) IEEE choice to make `0.0` and `-0.0` two different values.  
> **username_10** (2023-07-12): Signed zero does have some purpose -- if you take a negative value and divide it by a sufficiently large value to completely reduce its mantissa to 0 (due to limited bit precision), you have not lost the fact that it was originally negative.  This is occasionally important.  
> **username_11** (2023-07-12): Seems like you can avoid the former issue by just allowing the compiler to read (say) bigints, and then have logic that deals with the case where the literal describes a number too large to fit into whatever you're trying to store it in.  
> **username_12** (2023-07-12): This `-0` can easily be linted against, though. Nothing simpler than syntactic linters, after all.  
> **username_13** (2023-07-15): The `INT_MIN` issue may or may not be a problem for all languages, of course.  For example, part of my current language design is to have literals and expressions involving them default to an infinite-precision type until they interact with something that has a defined precision, e.g. by being assigned to a variable or used in an expression alongside something that can't be computed at compile time.  In such a language, this is no issue at all: `const INT_MIN = -(2147483648)` works just fine, but just needs a little more memory to compile.  OTOH, I do have negative literals....  
> **username_14** (2023-08-04): @occipita: I wonder why I haven't seem more languages adopt such an approach (though perhaps limiting the maximum precision to limit the amount of memory required for something like `(7 << 1000000) + 5 - (1 << 1000003)) + (1 << 1000000)`)?  Treating literals as their own type would seem to have a variety of advantages.  
---
### Answer by username_15 on 2023-07-10. Score: 17
### Do both (avoid abiguity or parenthesis)

The overloading of `-` for negative literals and negation causes ambiguity:[W](https://en.wikipedia.org/wiki/Plus_and_minus_signs#Uses_in_computing)

> In mathematics and most programming languages, the rules for the order of operations mean that −52 is equal to −25: Exponentiation binds more strongly than the unary minus, which binds more strongly than multiplication or division. However, in some programming languages (Microsoft Excel in particular), unary operators bind strongest, so in those cases `−5^2` is 25, but `0−5^2` is −25.

To avoid this (and to allow adjacent numerical tokens not causing subtraction) a language can use a separate character for negative literals. APL uses `¯` while J uses `_`.[W](https://en.wikipedia.org/wiki/Plus_and_minus_signs#Uses_in_computing)
> **Comments:**
> **username_16** (2023-07-11): one of those times when the solution is worse than the problem!  
> **username_17** (2023-07-11): off, but why would a programming language select an uncommon unicode token that's not present in usual keyboard layouts for such a commonly used sign as the unary minus? Seems to be a nightmare to type it.  
> **username_18** (2023-07-11): @Neinstein APL is designed to use a custom keyboard/font _anyway_. See [this image search](https://www.google.com/search?q=apl+keyboard&tbm=isch) for examples.  
> **username_19** (2023-07-13): See also in Javascript: `-64.0.toString(16)` evaluates to the number -40 vs `(-64.0).toString(16)` evaluates to the string '-40'  
> **username_8** (2023-07-13): @corvus_192 I'll do you one worse: in Swift, it's possible to devise a method `foo` for which `-0.foo()` , `(-0).foo()`, and `(-(0)).foo()` are all different. [Proof by example](https://swiftfiddle.com/3wyrh43uuzg6vflufcf762k3am)  
> **username_20** (2023-08-04): You could also avoid that ambiguity by making your precedence into a DAG requiring parenthesisation, instead of a list.  
---
### Answer by username_5 on 2023-07-10. Score: 12
The advantage of “unary negated positive literals” means avoiding a special case in your parser. You can treat the `-` in `-1234` the same way that you treat the `-` in `-x` or `-(x * y + z)`. It is for this reason that Jack Crenshaw's [Let's Build a Compiler](https://compilers.iecc.com/crenshaw/) chose that approach (as well as rewriting unary `-x` to binary `0-x`).

It does theoretically make the generated machine code slightly less efficient, but a simple [constant folding](https://en.wikipedia.org/wiki/Constant_folding) optimization will turn the expression into the desired negative constant.
---
### Answer by username_6 on 2023-07-11. Score: 10
## Referential transparency

This is probably the big one. If you have a negation operator rather than negative literals, you can replace `-5` with `-a` (where `a` is an integer variable) everywhere, and nothing else about the program will change.

This isn't true if `-5` is a token.

However...

## Interaction with other syntax

To quote from [the Haskell 2010 report, section 10.6](https://www.haskell.org/onlinereport/haskell2010/haskellch10.html#x17-18100010.6):

> The handling of the prefix negation operator, `-`, complicates matters
> only slightly. Recall that prefix negation has the same fixity as
> infix negation: left-associative with precedence 6. The operator to
> the left of `-`, if there is one, must have precedence lower than 6
> for the expression to be legal. The negation operator itself may
> left-associate with operators of the same fixity (e.g. `+`). So for
> example `-a + b` is legal and resolves as `(-a) + b`, but `a + -b` is
> illegal.

The negation operator also interacts with function application in ways that may be non-obvious to those coming from other languages; `f -1` doesn't mean `f (-1)`, it means `f - 1`.

To complicate things even further, Haskell doesn't have a unary plus operator, so `f (-1)` means negative one passed to the function `f`, but `f (+1)` means the operator section `(\x -> x + 1)` passed to `f`.

This is not unique to Haskell, but it's made more obvious because of the (otherwise extremely convenient) function call and operator section syntax. Some variants of Prolog have related issues. Now that I think of it, I doubt there are two Prolog implementations which parse the same way.

For comparison, C has 13 levels of operator precedence and many operators which can be both prefix and infix. Most C programmers never seem to find themselves needing to put parentheses around a negative integer, but the same can't be said of the prefix `*` operator.
> **Comments:**
> **username_7** (2023-07-11): Incidentally, GHC has [a syntactic extension](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/lexical_negation.html#extension-LexicalNegation) that avoids the complications you mention (allowing to write e.g. `9 + -8` and `map (- 2) [10..20]` and `abs -pi`). Basically, what it does is to treat `-` as unary negation iff it has an expression to its right without whitespace in between, but not an expression to its left without whitespace in between. Else, it's treated as infix minus operator. ...  
> **username_7** (2023-07-11): ...And if the expression to the right of the unary minus `-` is a number literal, the whole thing is syntactically treated as a single negative-valued literal, else it becomes a call to `negate`.  
---
### Answer by username_3 on 2023-07-14. Score: 5
## Exponentiation doesn't like negative literals

Languages with an exponentiation operator prefer to parse negative numbers as unary negation, because exponentiation has higher precedence than unary negation: `-3^2` should evaluate to `-9`, not to `+9`.
---
### Answer by username_4 on 2023-08-04. Score: 2
If `-` is a binary operator in your language, then you probably shouldn't make `-1` a single token. If you do, then `x-1` will be lexed as two tokens ─ an identifier `x` followed by a literal `-1` ─ and then you can't parse it as a binary operator expression like `x - 1`, unless your parser has a weird special case for treating negative number literals like postfix operators.

Yes, you also need a special case if you want to reject out-of-bounds integer literals as syntax errors while accepting `INT_MIN`, whose absolute value is out-of-bounds. But that's a considerably easier special case. For example, [here's how the Java Language Specification handles it](https://docs.oracle.com/javase/specs/jls/se20/html/jls-3.html#jls-3.10.1):

> The largest decimal literal of type `int` is `2147483648` (231).
>
> All decimal literals from `0` to `2147483647` may appear anywhere an int literal may appear. The decimal literal `2147483648` may appear only as the operand of the unary minus operator `-` (§15.15.4).
>
> It is a compile-time error if the decimal literal `2147483648` appears anywhere other than as the operand of the unary minus operator; or if a decimal literal of type `int` is larger than `2147483648` (231).

There is a similar rule for decimal literals of type `long`.
---
