Are bitwise operators that necessary?

For example, Alexander Stepanov (the designer and main implementor of the original C++ STL) writes in Elements of Programming Section 3.5 (Parametrizing Algorithms):

Using an operator symbol or a procedure name with the same semantics on different types is called overloading, and we say that the operator symbol or procedure name is overloaded on the type. For example, + is used on natural numbers, integers, rationals, polynomials, and matrices. In mathematics + is always used for an associative and commutative operation, so using + for string concatenation would be inconsistent.

In From Mathematics to Generic Programming he writes:

For many centuries, the symbol “+” has been used, by convention, to mean a commutative operation as well as an associative one. Many programming languages (e.g., C++, Java, Python) use + for string concatenation, a noncommutative operation. This violates standard mathematical practice, which is a bad idea. The mathematical convention is as follows: • If a set has one binary operation and it is both associative and commutative, call it +. • If a set has one binary operation and it is associative and not commutative, call it *. 20th-century logician Stephen Kleene introduced the notation ab to denote string concatenation (since in mathematics * is usually elided).

IIRC Herb Sutter and Bjarne Stroustrup have made similar complaints. There is a 2017 Stroustroup paper where he explains how to use C++ concepts efficiently and he makes similar remarks to those made by Alex.

Unless they want to write generic algorithms where they expect commutativity, in which case they require the Add trait (because it conveys commutativity), and users of that generic code pass it a string and get garbage out.

Sure, you can use all of the algorithms in the bitwise crate on all of the integer types to manipulate their bits.

If I want to manipulate the bits of an integers as raw bits, I should not need to do any conversions for that. E.g. if I want to do a parallel bit deposit on a signed integer, that should just work. I do not want a type error saying I have to convert it to an unsigned integer, nor do I want garbage out due to the wrong kind of shift being invoked.

Why is generic code that always works correctly and efficiently an anti-pattern?

If you find yourself wanting to do logical shifts on a signed int or arithmetic shifts on an unsigned int, it's a sign that you should rethink the design of your code.

Bits are just bits, logical shifts and arithmetic shifts are two different bit manipulation algorithms. Which one you use depends on how do you want to manipulate the bits. If you want to do integer arithmetic at the bit level, what you say might make sense, but arguably then the issue is that you are doing arithmetic at the wrong level of abstraction, e.g., you should be using a power of two function instead of shifting bits nitty willy to better express your intent.

1 Like

Calling them OpPlus or even Op+ would have been "cleaner".

@gnzlbg This is also my opinion.

If I want to manipulate the bits of an integers as raw bits, I should not need to do any conversions for that.

This is related to the point @madmalik made. I would say that the very fact you are speaking about "integers as raw bits" implies you are actually already doing some sort of implicit semantic conversion. Shifts are not natural operation on integer whether signed or not. Like all bitwise operations they are related to the decomposition of integers in base 2 which should not be any more relevant than base 16 or 7 as far as arithmetic is concerned. So again and contrary to what all mainstream languages did following C, I would argue types different from both signed and unsigned integers should be used when doing bitwise manipulation with explicit casts if you are switching between this and arithmetic.

2 Likes

Rust doesn’t have “integers” though, it has unsigned and signed (2’s complement) fixed-width binary integers, i.e. uN is defined as an N-long bitstring interpreted as an unsigned integer, with values from 0 to 2^N - 1, and usize / isize are defined as “newtypes” of uN / iN where N is the pointer width in bits.

Ai ai ai! Why do people want to get rid of my logical set operators?! ;_:

People use these things for integers?!

11 Likes

I hadn’t noticed they were also implemented by sets. This is a good point. Now this doesn’t mean mere methods wouldn’t do the job. And again + and * could be used instead. One may still want ^ for symmetric difference but this one is not a big deal as this character is not used for something else AFAIK.

I use bitwise operators far too frequently in so much code that I write. They’re such an essential tool in a systems programming language that not having them as convenient operators would be quite frustrating.

5 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.