3

It appears that the logical NOT operator ! has non-intuitive order of operations in arithemtic:

set.seed(42)
a  <-  sample(100, 3)
b  <-  sample(100, 3)
c  <-  sample(100, 3)
l  <-  (1:3) <= 2

a * !l - b * !l + c
# 0  0 29

# same expression, but with parentheses for explicit grouping order of operations
(a * !l) - (b * !l) + c
# 74 14 43

There must be something I do not understand about the ! operator in relation to * or conversion from logical to numeric?

cmo
  • 3,762
  • 4
  • 36
  • 64
  • 6
    Check the `?Syntax` for operator precedence . plus/minus comes before multiply/division – akrun Jun 14 '19 at 15:22
  • good idea @akrun, but i do not see anything informative about `!` operator precedence in `?Syntax` – cmo Jun 14 '19 at 15:23
  • It is there, the negation operator – akrun Jun 14 '19 at 15:27
  • 2
    I have *always* felt (and for a while, *thought*) that the negation operator should be as immediate as the negative operator, as locally as possibly. The concept of a greedy negator is just ... wrong, to me. Unfortunately, they did not consult me when they implemented it. (It is counter-intuitive to me that `(!F)+5` and `!F+5` are not the same thing ... even if an otherwise nonsensical expression.) Sigh. ``. Thanks @MrFlick. – r2evans Jun 14 '19 at 20:38

1 Answers1

3

Note that in R, the negation operator ! will apply to entire expression to the right of the operator until it gets to the end or encounters an expression with a lower precedence. It does not just negate the most immediate term. Recall also that 0 is treated as FALSE and any other number is TRUE. So observe

!0
# [1] TRUE
!5
# [1] FALSE
!5-5
# [1] TRUE
!5-3-2
# [1] TRUE
(!5)-3-2
# [1] -5

So you see in the case of !5-3-2 the negation isn't happening until after the 5-3-2 is evaluated. Without the parenthesis, the negation is the very last thing that happens.

So when you write

a * !l - b * !l + c

that's the same as

a * !(l - (b * !(l + c)))

Because all the operations have to happen to the right of the negation before the negation can occur.

If you want to negate just the l terms, you can do

a * (!l) - b * (!l) + c

This is a function of the operator precedence in R (see the ?Syntax help page for details). It's once of the last operators to be evaluated in the given expression.

Note that & and | have a lower precedence than ! so when you do

!a | !b & !c

that's the same as

(!a) | ((!b) & (!c))

so this roughly would be what you expect if you just stick to logical operators. It just gets a bit odd perhaps when you combine logical and arithmetic operators.

MrFlick
  • 195,160
  • 17
  • 277
  • 295
  • "the negation operator ! applies to value of the entire expression to the right of the operator" is true here but might be misleading, it seems to me to imply `!` has the weakest precedence. Most relevant in practice is that `&`, `&&`, `|` and `||` have lower precedence. – moodymudskipper Jun 17 '19 at 14:55
  • 1
    @Moody_Mudskipper Good point. I updated to clarify that it will stop when it hits on operator with lower precedence. – MrFlick Jun 17 '19 at 15:20