Asked  7 Months ago    Answers:  5   Viewed   43 times

I am confused as to when I should use Boolean vs bitwise operators

  • and vs &
  • or vs |

Could someone enlighten me as to when do i use each and when will using one over the other affect my results?



Here are a couple of guidelines:

  • Boolean operators are usually used on boolean values but bitwise operators are usually used on integer values.
  • Boolean operators are short-circuiting but bitwise operators are not short-circuiting.

The short-circuiting behaviour is useful in expressions like this:

if x is not None and == 42:
    # ...

This would not work correctly with the bitwise & operator because both sides would always be evaluated, giving AttributeError: 'NoneType' object has no attribute 'foo'. When you use the boolean andoperator the second expression is not evaluated when the first is False. Similarly or does not evaluate the second argument if the first is True.

Tuesday, June 1, 2021
answered 7 Months ago

== means equality, so the conditional reads as:

If pre-incremented $x equals 10, echo $x

Single = is assignment, where a variable is set to contain a value:

$word = 'hello';
$number = 5;
// etc.

echo "I said $word $number times!";

Regarding the increment opperators:

You'll see things like ++$x and $i-- as you learn PHP (and/or other languages). These are increment/decrement operators. Where they're positioned in relation to the variable they're operating on is important.

If they're placed before the variable, like ++$x, it's a pre-increment/decrement. This means the operation is performed before anything else can be done to the variable. If it's placed after, like $x++, it's a post-increment/decrement, and it means that the operation is performed afterward.

It's easiest to see in an example script:

$x = 5;

echo ++$x; // 6
echo $x++; // ALSO 6
echo $x; // NOW 7
Saturday, August 7, 2021
answered 4 Months ago

Why not simply trying something like this using list comprehensions:

c1 = ((1, 0, 0, 0, 1, 1), (1, 1, 0, 0, 0, 1), (1, 1, 1, 0, 0, 0))
c2 = ((1, 0, 1, 1, 0, 0), (0, 1, 0, 1, 1, 0), (0, 1, 1, 0, 1, 0))

print('Bitwise or:  ', [[k | l for k, l in zip(i, j)] for i, j in zip(c1, c2)])
print('Bitwise and: ', [[k & l for k, l in zip(i, j)] for i, j in zip(c1, c2)])
Tuesday, August 24, 2021
Arif Nadeem
answered 4 Months ago

This depends on a lot of things, but mostly what (if anything) you tell the compiler to optimize for.

If the compiler is set to optimize for size (smallest bytecode), then sometimes it will use XOR in seemingly strange places. For instance, the variable length encoding scheme X86 uses can set a register to 0 by XOR'ing itself in fewer bytes of code than would be required using the MOV instruction.

Consider the code that uses XOR:

if ( (val ^ ~0U) == 0 )  /* 3-bytes to negate and test (x86) */

    XOR eax,0FFFFFFFFh requires 3-bytes AND sets/clears the Zero Flag (ZF)

Now, consider the code that uses NOT:

if ( (~val) == 0)        /* 4-bytes to negate and test (x86) */

    NOT eax is encoded into a 2-byte instruction, but does not affect CPU flags.

    TEST eax,eax adds an additional 2-bytes, and is necessary to set/clear the Zero Flag (ZF)

NOT is also a simple instruction, but since it does not affect any CPU flags, you must issue a TEST instruction afterwards to use it for branching as seen in your code. This actually produces larger bytecode, so a smart compiler set to optimize for size would probably try to avoid using NOT. How many cycles both of these instructions together take to complete varies between CPU generation, and a smart compiler would also factor this into its decision making when told to optimize for speed.

If you are not writing hand-tuned assembly, it is best to use whatever is clearest to a human and hope that the compiler is smart enough to choose different instructions/scheduling/etc. to optimize for size/speed as requested at compile-time. Compilers have a smart set of heuristics they use to choose and schedule instructions, they know more about the target CPU architecture than the average coder.

If you find out later that this branch really is a bottleneck and there is no higher-level way around the problem, then you could do some low-level tuning. However, this is such a trivial thing to focus on these days unless you are targeting something like a low-power embedded CPU or memory limited device. The only places I have ever squeezed out enough performance by hand-tuning to make it worthwhile were in algorithms that benefited from data parallelism and where the compiler was not smart enough to effectively utilize specialized instruction sets like MMX/SSE.

Tuesday, August 31, 2021
answered 3 Months ago

You could always add a & ((1<<32) - 1) mask to limit the number to 32-bits before performing any operation, e.g.

class Int32(int):
    def __neg__(self):
        return Int32(int.__neg__(self) & ((1 << 32) - 1))
    def __rshift__(self, other):
        if self & (-1 << 31):
             retval = int.__rshift__(int.__sub__(self, 1<<32), other)
             return Int32(retval & ((1 << 32) - 1))
             return Int32(int.__rshift__(self, other))

>>> -Int32(5)
>>> (-Int32(5)) >> 1
Monday, October 18, 2021
answered 2 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :