Operator precedence of unary minus (-) and exponentiation (^) outside vs. inside function
Because of the surprisingly low precedence of the "unary minus" (-
) operator, combined with R's convention for raising negative numbers to fractional powers. -27^(1/3)
is read as "calculate 27^(1/3) (==3), then invert the sign". If we instead compute (-27)^(1/3)
, we get NaN
, because a negative number can't reliably be raised to a fractional power. Whether the evaluation is inside or outside of a function is a red herring; the issue is whether (-27) is evaluated as an expression first (which it is if you assign it to a function argument or a variable, or put it in parentheses).
The precedence of unary minus is discussed on this mailing list thread from 2009 and R FAQ 7.33, and more generally in this math.stackexchange question. There's a (rather technical) explanation of why a negative value to a fractional power is NaN
in ?"^"
:
Users are sometimes surprised by the value returned, for example
why(-8)^(1/3)
isNaN
. For double inputs, R makes use of IEC
60559 arithmetic on all platforms, together with the C system
functionpow
for the^
operator. The relevant standards
define the result in many corner cases. In particular, the result
in the example above is mandated by the C99 standard. On many
Unix-alike systems the commandman pow
gives details of the
values in a large number of corner cases.
`x^(1/3)` behaves differently for negative scalar `x` and vector `x` with negative values
I'm not looking for a workaround e.g.
function(x) {sign(x) * (abs(x)) ^ (1/3)}
.
I'm interested in an answer that explains what is happening differently to the vector than to the negative value when provided as a numeric scalar.
how does the
^
operator think differently about vectors and scalars?
You seem to believe that c(-0.2, 1)^(1/3)
translates to c(-0.2^(1/3), 1^(1/3))
. This is incorrect. Operator ^
is actually a function, that is, (a) ^ (b)
is as same as "^"(a, b)
. Therefore, the correct interpretation goes as follows:
c(-0.2, 1)^(1/3)
=> "^"(c(-0.2, 1), 1/3)
=> c( "^"(-0.2, 1/3), "^"(1, 1/3) )
=> c( (-0.2)^(1/3), (1)^(1/3) )
=> c( NaN, 1 )
Now, why doesn't -0.2^(1/3)
give NaN
? Because ^
has higher operation precedence than +
, -
, *
and /
. So as it is written, it really implies -(0.2^(1/3))
instead of (-0.2)^(1/3)
.
The lesson is that, to avoid buggy code, write your code as (a) ^ (b)
instead of just a ^ b
.
Additional Remark:
I often compare ^
and :
when teaching R to my students, because they have different behaviors. But they all show the importance of protecting operands with brackets.
(-1):2
#[1] -1 0 1 2
-1:2
#[1] -1 0 1 2
-(1:2)
#[1] -1 -2
2*3:10
#[1] 6 8 10 12 14 16 18 20
(2*3):10
#[1] 6 7 8 9 10
2*(3:10)
#[1] 6 8 10 12 14 16 18 20
See ?Syntax
for details of operator precedence.
How to deal with unary minus and exponentiation in an expression parser
Unless you have unusual requirements, putting both unary minus and exponentiation in the same non-terminal will work fine, because exponentiation is right-associative: (Yacc/bison syntax)
atom: ID
| '(' expr ')'
factor
: atom
| '-' factor
| atom '^' factor
term: factor
| term '*' factor
expr: term
| expr '+' term
| expr '-' term
Indeed, exponentiation being right-associative is virtually required for this syntax to be meaningful. Consider the alternative, with a left-associative operator.
Let's say we have two operators, ⊕ and ≀, with ⊕ being left associative and binding more tightly than ≀, so that ≀ a ⊕ b
is ≀(a ⊕ b)
.
Since ⊕ is left associative, we would expect a ⊕ b ⊕ c
to be parsed as (a ⊕ b) ⊕ c
. But then we get an oddity. Is a ⊕ ≀ b ⊕ c
the same as (a ⊕ ≀b) ⊕ c)
or the same as a ⊕ ≀(b ⊕ c))
? Both options seem to violate the simple patterns. [Note 1]
Certainly, an unambiguous grammar could be written for each case, but which one would be less surprising to a programmer who was just going by the precedence chart? The most likely result would be a style requirement that ≀ expressions always be fully parenthesized, even if the parentheses are redundant. (C style guides are full of such recommendations, and many compilers will chide you for using correct but "unintuitive" expressions.)
Notes:
- If you use precedence declarations, you'll get
a ⊕ ≀(b ⊕ c))
, which might or might not be intuitive, depending on your intuitions.
R exponentiating mystery
Do you get the correct answer though? The following works:
-2.108844 ^ 0.9891197
#> [1] -2.091793
And gives the same result as the following:
-(2.108844 ^ 0.9891197)
#> [1] -2.091793
But look what happens if I move the negative sign inside the brackets:
(-2.108844) ^ 0.9891197
#> [1] NaN
Note that R quite appropriately gives NaN
because I am raising a negative number to a fractional power.
So the answer to your question is that due to operator precedence, the R parser is interpreting
-2.108844 ^ 0.9891197
as
-(2.108844 ^ 0.9891197)
Which is giving you the wrong answer when you type the numbers directly.
JavaScript exponentiation unary operator design decision
I don't understand this design decision.
Read more about it at https://esdiscuss.org/topic/exponentiation-operator-precedence, https://esdiscuss.org/topic/power-operator-why-does-2-3-throws, https://github.com/rwaldron/tc39-notes/blob/master/es7/2015-09/sept-23.md#exponentiation-operator and https://github.com/rwaldron/tc39-notes/blob/master/es7/2015-09/sept-24.md#exponentiation-operator.
Who's really going to be surprised that
-x ** 2
is negative?
Enough people to matter. Some relevant quotes from the above resources:
- "making
**
bind tighter than unary operators would breakx**-2
. And making it sometimes tighter and sometimes looser would be too confusing and lead to other opportunities for precedence inversion." - Waldemar Horwat - "Given the conflict between the history of
**
in other languages, [and] the general pattern that unary binds tighter than binary, any solution at this point will confuse many people." - Mark S. Miller - "acknowledge the prospect of significant whitespace:
-x**2 === -(x ** 2)
and-x ** 2 === (-x) ** 2
" - Alexander Jones - "The problem is, however rare unary minus before an exponentiation expression may be, the lack of superscript-with-smaller-font sugests that
-
binds tighter than**
. And indeed apart from dot (a special form whose right operand must be a lexical identifier-name) and square brackets (which isn't an infix operator per se), unary operators bind tighter than binary in JS as in C and other C-derived languages." - Brendan Eich - "For math it seems obvious that
-52
. But for-5 ** 2
, because of the whitespace around the infix operator. Even without space,-
seems to be part of the literal." - Dave Herman - [Regarding programming language precedence], "effectively zero people have an intutition about this from other languages. Agree people have an itutition that
**
is the exponentiation operator. But people usually try to avoid dark corners so they never develop an intuition for negative bases." - Dave Herman
In Javascript
'1'+ 2
is'12'
and'1'-2
is-1
but-1**2
raises an error because it could be ambiguous?
Well they put considerably more effort in the design of extensions to the language today :-) It's the best solution that they could reach consensus for.
What does the unary operator - do on unsigned data types in C/C++ (and on different compilers)?
The relevant quote from the Standard is actually this:
(§5.3.1/8) The operand of the unary - operator shall have arithmetic or unscoped enumeration type and the result is the negation of its operand. Integral promotion is performed on integral or enumeration operands. The negative of an unsigned quantity is computed by subtracting its value from 2n, where n is the number of bits in the promoted operand. The type of the result is the type of the promoted operand.
(This is from C++11; it used to be 5.3.1/7 in older versions.)
So -num
will be evaluated as 2CHAR_BIT*sizeof(num) - num (‡). The result will be of the same type as the operand (after integer promotion), i.e. it will also be unsigned.
I just tested with GCC and it seems to perform the operation in precisely the way described by the Standard. I'll assume this is the case for Visual C++ as well; otherwise it's a bug.
(‡) This formula assumes that the relevant number of bits corresponds to the size (in bits) of the variable in memory. As Keith Thompson points out in the comment, this can't be true if there are padding bits (i.e. when not all bits participate in the representation of the numerical value, which is possible according to §3.9.1/1). On a system that uses more bits to store the value than are used to represent the numerical value, the formula will not be accurate. (I, personally, am not actually aware of any such system, though.)
Related Topics
Knitr Compile Problems with Rstudio (Windows)
R Data.Table Fread Command:How to Read Large Files with Irregular Separators
Ggplot2 Scale_X_Log10() Destroys/Doesn't Apply for Function Plotted via Stat_Function()
How to Make Shiny's Input$Var Consumable for Dplyr::Summarise()
Row Not Consolidating Duplicates in R When Using Multiple Months in Date Filter
Convert String of Anyformat into Dd-Mm-Yy Hh:Mm:Ss in R
Convert Factor to Date Class for Multiple Columns
How to Know Which Cluster Do the New Data Belongs to After Finishing Cluster Analysis
Making Multiple Style References in Google Maps API
Meaning of Tilde and Dot Notation in Dplyr
Shortcut Using Lm() in R for Formula
How to Apply Dplyr's Select(,Starts_With()) on Rows, Not Columns
How to Scrape Items Together So You Don't Lose the Index
Converting Yearmon Column to Last Date of the Month in R