18

This is a pretty trivial question that I haven't been able to find the answer to.

Here is the problem. I have the following array:

vals = [-5, 2]

And I want to check whether val[0] or val[1] is greater than 0. If either is true, then I should output True.

My immediate thought was to use; (vals[1] or vals[0]) > 0) but I'm finding that (5 | -2) > 0 is False where (5 or -2) > 0 is True

Any clarification would be much appreciated.

madsthaks
  • 1,801
  • 3
  • 20
  • 40

6 Answers6

44

What is the difference between or and |?

or is a logical or and | is a bitwise or logical inclusive or.

The logical or

The logical or in python returns the first value that is true.

Example:

>>> None or False or 5
5
>>> -5 or 2
-5

The bitwise or logical inclusive or

The bitwise or logical inclusive or is represented by the | operator in python and creates a number where all bits are set that are set in at least one of the given numbers.

Example:

  • 2 is in binary 0010
  • 4 is in binary 0100

A logical or between the two results in 0110 which is 6.

>>> 2 | 4
6

How a negative number is stored is generally implementation specific. However on most systems a negative number is stored by creating the two's complement of the positive number by inverting each bit and adding 1.

That number in bitwise ore two any other number still results in a negative number:

>>> -5 | 2
-5

Neither of the two solves your problem

While using

(vals[1] or vals[0]) > 0

seems to work, it fails when you flip the values:

>>> vals = [2, -5]
>>> (vals[1] or vals[0]) > 0
False

You should check both values seperatly

>>> vals = [-5, 2]
>>> vals[0] > 0 or vals[1] > 0
True

For a larger input this may be inconvenient. You should use any with a generator expression:

>>> any(x > 0 for x in vals)
True
kalehmann
  • 4,123
  • 6
  • 18
  • 34
  • 1
    These are Python ints, not native CPU integers; I'm pretty sure they're explicitly specified to use two's complement. (That said, I'm having a surprisingly hard time locating such a specification. The closest I've found is [this wiki page](https://wiki.python.org/moin/BitwiseOperators), which is explicit enough but perhaps not fully authoritative, and [an oblique remark in the docs](https://docs.python.org/3/reference/expressions.html#unary-arithmetic-and-bitwise-operations) saying that "The bitwise inversion of `x` is defined as `-(x+1)`.") – Ilmari Karonen Mar 18 '19 at 21:53
  • 1
    Yeah, the [docs for the built-in types](https://docs.python.org/3/library/stdtypes.html) have several hints suggesting that. – kalehmann Mar 18 '19 at 22:27
  • Good find. In fact, that page explicitly says that "The result of bitwise operations is calculated as though carried out in two’s complement with an infinite number of sign bits." – Ilmari Karonen Mar 18 '19 at 23:07
14

You want the any function:

>>> any(x > 0 for x in vals)

x | y computes the bitwise OR of the two values, while x or y evaluates to the first "truthy" value. In both cases, the result is then compared to 0: (x or y) > 0 and (x | y) > 0.

What you want to compare each value to zero (as necessary), with

vals[0] > 0 or vals[1] > 0

If you had three values, you'd write

vals[0] > 0 or vals[1] > 0 or vals[2] > 0

The any function generalizes this to a list of any size, without the need to decide how many terms to or together based on the size of the list.

chepner
  • 389,128
  • 51
  • 403
  • 529
5

To answer this question, I have to explain about Two's Complement.

BINARY REPRESENTATION OF NUMBERS

So you know how internally, an integer like 5 is represented as a binary string

00000000000000000000000000000101

How do you imagine you'd represent a negative number?

Well, here's what we want to do:

  • Addition should work the same with negative numbers and positive numbers; i.e. you do the same steps to add 4 + 9 as 4 + -9.

  • Integer overflow shouldn't break mathematics; i.e. MAX_VALUE + 1 == MIN_VALUE, MIN_VALUE - 1 == MAX_VALUE

So what we do is called "Two's Complement."

TWO'S COMPLEMENT

To represent a negative number, take its absolute value, bitflip every bit, and add 1.

So if the positive number is 5

00000000000000000000000000000101

the negative number -5 is

11111111111111111111111111111011

Essentially, this means we pick the number 01111111111111111111111111111111 to be the largest positive number, and all numbers after that are negative.

SO WHAT DOES (5 | -2) MEAN?

The | is the bitwise or operator. Given two numbers, it takes every bit and or's them together, constructing a new number where a digit is 1 if the digit in that position in wither or both of the two original numbers is 1, and 0 otherwise. The calculation looks like this:

   5 -> 00000000000000000000000000000101
| -2 -> 11111111111111111111111111111110
----    --------------------------------
        11111111111111111111111111111111 -> -1

So as you can see, 5 | -2 = -1 < 0.

WHAT ABOUT (5 or -2)?

The "or" operator takes two values, casts them to booleans and or's them together. This is important: it doesn't or the values, it returns the first value that is "truthy" -- in other words, if you put it in an if statement, it would run.

The only integer that isn't "truthy" is 0. Therefore (5 or -2) returns the first non-zero integer of 5 and 2, which is 5 > 0. So 5 or -2 = 5 > 0.

Q Science
  • 101
  • 6
3

| is a bitwise OR, and Python uses two's complement representation for integers. Evaluating 5 | -2 gives:

  ... 0000 0000 0000 0101 (+5)
| ... 1111 1111 1111 1110 (-2)
──────────────────────────────
= ... 1111 1111 1111 1111 (-1)

And -1 is not greater than zero, so (5 | -2) > 0 is false.

or is a logical OR. Unlike in other languages where this operator returns a Boolean (True/False) value, Python defines x or y as being equivalent to x if x else y (except that x is evaluated only once). Note that any nonzero numeric value is “truthy” in Python, so if x≠0, then x or y evaluates to x.

>>> 5 or -2
5
>>> -2 or 5
-2

That (5 or -2) > 0 evaluates to True was a stroke of luck from having the positive number first. In the other order, you would have gotten False.

In general (x or y) > 0 is not equivalent to (x > 0) or (y > 0), which is what you meant.

dan04
  • 77,360
  • 20
  • 153
  • 184
  • I feel dumb, but why is all `1`s equal to `-1` and not 2^16 or whatever? – user1717828 Mar 18 '19 at 18:47
  • 2
    @user1717828 because the usual convention is that the most significant bit is taken as negative; so -2^31 for a 4-byte integer. This allows for the storage of a good range of positive and negative numbers. If all the bits were taken as positive, you'd only be able to represent positive numbers, which in many contexts would be less useful. – Dawood ibn Kareem Mar 18 '19 at 19:20
  • Also, Python takes this to the extreme; `-1` is represented by an _infinite number_ of `1`s. (It's compressed in memory, in the CPython (reference) implementation.) – wizzwizz4 Mar 18 '19 at 21:23
2

When you do (5 | -2), you're doing a bitwise-OR. That will preserve the negation bit in the numbers. Therefore, you'll still have a negative number.

The (5 or -2) is a logical-OR, to the Python interpreter will extend that to the next logical operator (the greater-than).

jhill515
  • 697
  • 8
  • 20
1

They are two completely different operations so that is expected.

To illustrate, here's a small shell log:

In [1]: 5 or -2
Out[1]: 5

In [2]: 5 | -2
Out[2]: -1

The or operator returns the first non-zero (non-None, non-False, etc.) value.

The | operator does a bitwise or. To illustrate:

In [3]: bin(5)
Out[3]: '0b101'

In [4]: bin(-2)
Out[4]: '-0b10'

In [5]: bin(5 | -2)
Out[5]: '-0b1'
Wolph
  • 69,888
  • 9
  • 125
  • 143