Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Operator precedence in negative exponentials #1530

Open
drathier opened this Issue Dec 16, 2016 · 11 comments

Comments

Projects
None yet
8 participants
@drathier
Copy link

drathier commented Dec 16, 2016

-2^2 evaluates to a positive number, unlike in (most) mathematics, since unary - has higher precedance than ^ in Elm.

I expected it to evaluate to a negative number. It is extra easy to make a mistake since -2^n changes sign for odd/even n, so half the numbers seem correct.

Can we change the operator precedance?

EDIT: it should fit nicely between ^ and *, like D-lang and Python does it.

@process-bot

This comment has been minimized.

Copy link

process-bot commented Dec 16, 2016

Thanks for the issue! Make sure it satisfies this checklist. My human colleagues will appreciate it!

Here is what to expect next, and if anyone wants to comment, keep these things in mind.

@mgold

This comment has been minimized.

Copy link
Contributor

mgold commented Dec 17, 2016

Some prior art (note that many languages use ^ as bitwise xor, which will yield -4 in this particular case but not in general):

$ ghci
Prelude> -2**2
-4.0

$ python
>>> -2**2
-4

$ irb # Ruby
irb(main):001:0> -2**2
=> -4

# JavaScript does not have an exponentiation operation, so avoids the confusion
$node
> Math.pow(-2, 2)
4
> -Math.pow(2, 2)
-4

$ elm repl
> -2^2
4 : number

The proposal, then, is to treat -2^2 as -(2^2) instead of (-2)^2.

@lydell

This comment has been minimized.

Copy link

lydell commented Dec 17, 2016

ES2016:

> 2 ** 2
4
> -2 ** 2
SyntaxError: unparenthesized unary expression can't appear on the left-hand side of '**'
> (-2) ** 2
4

(The above was done in the Firefox 52 web console.)

@evancz

This comment has been minimized.

Copy link
Member

evancz commented Dec 19, 2016

@mgold, can you say -2 ** 2 with spaces in Haskell, Python, and Ruby? In other words, do all these languages treat ** as part of a number literal, or is it an infix operator?

If it's an infix operator, this seems pretty weird to me. Like saying -2 + 3 should be -(2 + 3) and give back -5

For my future reference, here is a discussion of this question.

@mgold

This comment has been minimized.

Copy link
Contributor

mgold commented Dec 19, 2016

can you say -2 ** 2 with spaces in Haskell, Python, and Ruby?

In all three of those languages, the version with spaces behaves identically to the version without.

It seems to be an infix operator accepting arbitrary numeric expressions:

$ python
>>> (2*2) ** (1+1+1)
64

The Dr. Math article you linked to is pretty compelling. I'd be in favor of this change.

@evancz evancz added the request label Mar 7, 2018

@toonn

This comment has been minimized.

Copy link

toonn commented Aug 21, 2018

I'm surprised nothing has been done about this for so long. To further clarify: in haskell there are no negative number literals. Unary - is an operator and exponentiation is an operator that happens to take precedence over the former. I'd expect everyone to be familiar with this order of operations from maths classes.

The linked article does mention Excel uses the same (non-standard) precedence for unary minus. So there is non insignificant prior art but I hope the whole of mathematics and the majority of programming languages can convince the developers w.r.t. what the least surprising behavior is.

This backwards incompatible change is something that'll only become harder to implement the longer it remains neglected.
That's why I decided to bump the issue because the current situation means people can't rely on the precedence of unary minus and therefore need lots of parentheses. Whether the issue is closed or resolved we will finally be able to rely on the precedence rather than parentheses.

@Qqwy

This comment has been minimized.

Copy link

Qqwy commented Oct 3, 2018

I would also accept -2^2 raising a compile-error or warning asking people to clarify what they mean ((-2)^2 vs -(2^2), but this definitely should not implicitly have surprising behaviour.

@drathier

This comment has been minimized.

Copy link
Author

drathier commented Jan 3, 2019

I've since learned two definitions of unary negation: -x is defined as either 0 - x or -1 * x.

-2^2 := 0 - 2^2 or -1 * 2^2. Similarly,
-2*2 := 0 - 2*2 or -1 * 2*2, and
-2+2 := 0 - 2+2 or -1 * 2+2. It seems to work for more complex expressions too:
-2*3+4 := 0 - 2*3+4 or -1 * 2*3+4 and even for anti-commutative operators:
-2-3 := 0 - 2-3 or -1 * 2-3.

I can't think of an example where the two definitions would disagree. D-lang puts unary negation between exponentation and multiplication, and so does Python.

@rlefevre

This comment has been minimized.

Copy link

rlefevre commented Jan 3, 2019

There are a lot of different implementations of exponentiation associativity and negation precedence.

From https://codeplea.com/exponentiation-associativity-options (with elm added):

Left or Right Associativity of the Exponentiation Operator?

What Code Tested Result Associativity
Elm 2^2^3 256 right-assiciative
Bash 2**2**3 256 right-associative
C++ a^b^c 64 left-associative
DuckDuckGo 2^2^3 256 right-associative
Excel 2^2^3 64 left-associative
EtherCalc 2^2^3 64 left-associative
Fortran 2**2**3 256 right-associative
Google 2^2^3 256 right-associative
Google Sheets 2^2^3 256 right-associative
Hand-held Calculators     Varies
Lua 2^2^3 256 right-associative
Matlab 2^2^3 64 left-associative
Octave 2^2^3 64 left-associative
Perl 2**2**3 256 right-associative
PostgreSQL 2^2^3 64 left-associative
Python 2**2**3 256 right-associative
Ruby 2**2**3 256 right-associative
Tcl 2**2**3 256 right-associative
WolframAlpha 2^2^3 256 right-associative

Variable Negation or Exponentiation First?

What Code Tested Result First Step
Elm -2^2 4 negation
Bash -2**2 4 negation
DuckDuckGo -2^2 4 negation
Excel -2^2 4 negation
EtherCalc -2^2 4 negation
Fortran -2**2 -4 exponentiation
Google -2^2 -4 exponentiation
Google Sheets -2^2 4 negation
Handheld Calculators -2^2   Varies
Lua -2^2 -4 exponentiation
Matlab -2^2 -4 exponentiation
Octave -2^2 -4 exponentiation
Perl -2**2 -4 exponentiation
PostgreSQL -2^2 4 negation
Python -2**2 -4 exponentiation
Ruby -2**2 -4 exponentiation
Tcl -2**2 4 negation
WolframAlpha -2^2 -4 exponentiation

Even different versions of TI handheld calculators may have different implementations.

AFAICT, Elm for now is right-associative for exponentiation, but put negation precedence over exponentiation, like bash, DuckDuckGo, Google sheets and Tcl.

I believe that no implementation is really wrong, because there is no standard, as programming languages (and text inputs) are not exactly mathematical notation.

That said, most likely because it is closer to mathematical notation, most recent implementations these days seem to use right associativity for exponentiation and higher precedence of exponentiation (not addition) over negation (Haskell, Wolfram, Python, Julia, ...). So this might be currently the most expected implementation and the one to favor for new languages (except those that purposely want to maintain compatibility with some legacy implementations like excel or matlab, or those that prefer to raise a warning because of the ambiguity).

@drathier

This comment has been minimized.

Copy link
Author

drathier commented Jan 3, 2019

Does that mean 4^2^-1 = 2? Should 2^-1^2 parse as 2^(-1)^2?

@rlefevre

This comment has been minimized.

Copy link

rlefevre commented Jan 3, 2019

Most implementations that use right associativity for exponentiation and higher precedence of exponentiation over negation (tested on google, wolfram and python) would interpret those as:

  • -2^2^3 = -(2^(2^3)) = -256
  • 4^2^-1 = 4^(2^(-1)) = 2
  • 2^-1^2 = 2^(-(1^2)) = 0.5

Currently elm returns:

  • -2^2^3 = 256
  • 4^2^-1 = error (unknown operator)
  • 2^-1^2 = error (unknown operator)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.