I cannot think of a reason why the designers chose to deviate from the principle that single is bitwise and double is logical here,
That's not the principle in the first place; once you realize that, it makes more sense.
The better way to think of &
vs &&
is not binary and Boolean. The better way is to think of them as eager and lazy. The &
operator executes the left and right side and then computes the result. The &&
operator executes the left side, and then executes the right side only if necessary to compute the result.
WhenMoreover, instead of thinking about "binary" and "Boolean", think about what is really happening. The "binary" version is just doing the Boolean operation on an array of Booleans that has been packed into a word.
So let's put it together. Does it make any sense to do a lazy operation on an array of Booleans? No, because there is no "left side" to check first. There are 32 "left sides" to check first. So we restrict the lazy operations to a single Boolean, and that's where your intuition that one of them is "binary" and one is "Boolean" comes from, but that is a consequence of the design, not the design itself!
And when you think of it that way, it becomes clear why there is no !!
and no ^^
. Neither of those operators have the property that you can skip analyzing one of the operands; there is no "lazy" not
or xor
.
Other languages make this more clear; some languages use and
to mean "eager and" but and also
to mean "lazy and", for instance. And other languages also make it more clear that &
and &&
are not "binary" and "Boolean"; in C# for instance, both versions can take Booleans as operands.