Home > database >  `x^(1/3)` behaves differently for negative scalar `x` and vector `x` with negative values
`x^(1/3)` behaves differently for negative scalar `x` and vector `x` with negative values

Time:07-01

R seems pretty comfortable with computing the odd root of a negative number

-0.2^(1/3) # returns a good number 
# [1] -0.5848035

but something weird happens if you raise a vector to the 1/3.

c(-0.2, 1)^(1/3) # returns an NA for the first element
# [1] NaN   1

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.

I'm not looking for a workaround e.g. function(x){sign(x)*(abs(x))^(1/3)}. This answer seems to point in a good direction... how does the "^" operator think differently about vectors and scalars?

CodePudding user response:

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.

  • Related