Understanding the power of bitwise operators
deusinmachina.net
deusinmachina.net
If I may suggest, stop insulting your readers (e.g., several places implying reader does or does not do something 'normally', or not know something).
In cases where an error is well known, describe the error so it is understood you are using it as an example, and not an actual error (e.g., Null character (�) a black diamond with white question mark in the middle).
Single bit is the most often used type of data (vs "single bit is almost completely useless"). Your lights are on, or off; you are dead or alive; it is today or not, vast majority of network flags in various protocols are single bits.
There are a lot of weasel words throughout the post. If you plan to educate, uncertainty reduces comprehension and retention. There are some absolute statements are made too, but might rightfully questioned (e.g., [b]inary is the only language computers understand). Make sure your absolutes are truly absolutes.
Keep up the good work.
The description in the linked article is wrong, that's not a null character (U+0000) or ASCII's NUL. That black diamond symbol is U+FFFD the Unicode Replacement Character, it means "Something went wrong, so instead here is this symbol". For example if your decoder algorithm gets some gibberish and you can't or won't accept errors in decoding, a correctly designed decoder should produce U+FFFD each time, this is what Rust's String::from_utf8_lossy and String::from_utf16_lossy are doing.
U+FFFD was carefully chosen because it's not anything. It's not a letter, or a digit, it's not some ASCII control character, it's not a separator in any known writing system or protocol, it has no defined pre-existing purpose, which means if bad guys can trick your bad software into injecting U+FFFD into some data, chances are they achieved nothing of value.
I'm not getting sucked back into this. There comes a point when you're just staring at the alphabet letters and thinking "TF am I DOING?"
A 64 bit register doing XOR is really 64x parallel XOR happening in parallel each of size 1 bit.
A shift, rotate are just fancy movement operators and exist in the SIMD space.
Good bitwise programming IMO requires a full embrace of this parallel mindset. See the Chess Programming Wiki for all sorts of things that 64x parallel bits can represent.
In particular, a 8x8 chessboard maps well to 64 bits.
-------
No really. Do remember that one of the OG SIMD computers, the CM2 (connection machine 2) was a 1-bit core but SIMD across thousands of cores (bits). And a lot of research and understanding of modern parallel concepts comes from the Connection Machine.
All About XOR: https://accu.org/journals/overload/20/109/lewin_1915/
"While at first, they might seem obscure, unhelpful, or tools for people who write in low level programming languages, they do serve a purpose. "
But then... Instructions set, assembly language...
So I wrote some straight C, some nested for loops for row/column of the area to be patterned. I used row % 4, column % 4 to map to the appropriate bit within the Uint16 "pattern". Some bit shifting, masking and I knew whether to fill the pixel with foreground or background colors.
I always enjoy bitwise operations. (There's I guess the classic programmer problem of swapping the values of two numbers in two registers in place.)
Firstly, no one says that bitwise operators don't serve a purpose. Secondly, this is an interesting way to start an article that then goes on to explain how useful bitwise operators are for low level programming.
1110 >> 4 -> 0000 1110
For anyone interested in binary chess math:
https://www.chessprogramming.org/Bitboards#General_Bitboard_...
https://www.techiedelight.com/bit-hacks-part-2-playing-kth-b...
Incidentally, deciphering some unknown bitwise expression is a task that ChatGPT excels at. For example, try asking it to do a stepwise breakdown and summary of:
unsigned int a, b, mask, r;
r = a ^ ((a ^ b) & mask);
and use fixed fonts for all the numbers.
Also, in INTERCAL the character is called “shark” or “sharkfin”.
The real reason for the ubiquity of XOR in computer science is that it corresponds to addition of two bits in the field GF(2). If that sounds too complicated for your purposes, sorry.
Ehhh, GF(2) and even GF(5) are actually pretty easy to describe.
GF(2) is arithmetic modulo 2.
0+0 == 0
0+1 == 1
1+0 == 1
1+1 == 10, except we can only carry one bit in GF(2). Like an odometer, we can only keep the bottom bit, so the "1" drops off. 1+1 == 0.
Hey look, its XOR. The end.
----------
A "Field" in Abstract mathematics is simply a number-system that has add, additive-inverse (aka: subtraction), multiply, and multiply-inverse (aka: division). Note that "subtraction" and "division" are just simplifications of the concept, to be a true inverse, all inputs and outputs (domains-and-ranges) must be in the field.
So I proved that XOR is GF(2)'s addition. Funny note, 1 is also -1 (negative 1), aka the additive inverse of 1. And it turns out that XOR is _also_ the additive inverse (aka: subtraction). You see, -1 + 1 == 0, which happens to be 1+1 in GF(2). Ain't modulo math fun?
-----------
Multiplication is just AND btw.
0 * 0 == 0
0 * 1 == 0
1 * 0 == 0
1 * 1 == 1
The multiplicative inverse of 1 is... 1. That is, 1 * 1 equals 1. This one is a bit of a tautology, but it matches the technical definition of multiplicative-inverses (aka: division). This is why I prefer GF(5), because we get a less-trivial multiplicative-inverse.
----------
GF(5) is how I prefer to teach Galois fields. GF(2) and GF(3) are too simple. GF(5) is complex enough that the student actually has to learn Galois fields to understand it, but its simple enough that you can probably teach it to someone within 20 minutes.
There's a whole generation of programmers that aren't interested in, and don't need, "low level" bitwise operations. I would claim that's a "good thing", since it means they're letting someone else do the "boring stuff" by using libraries, allowing them to spend more time solving higher level problems.
When I was going to school, some sort of assembler was the first language you learned. These days, it's usually Python or Java. With a "not real programmers" silliness aside, I hope the tedium continues to decline, and is left for those interested in it, increasing productivity for everyone.
But it's worth understanding them even if you never need to use them. Much like understanding how a computer works at a low level, that kind of knowledge helps contextualize higher-level things and puts additional tools in your toolbox.
GFX -> HTML&CSS -> JS -> WTF!
They can be great programmers in JS even but have little to no computer science behind it and more a design background.
In modern programming, bit-banging can usually be avoided. C++ and Rust both have bit fields in structs, and generics for sets of bits. If you're working on some data structure with AND, OR, and shift operations, that's kind of retro today. The compiler is probably better at doing that stuff than you are.
I’d been using the RPN/scientific mode for ever but never new about Programmer mode.
I’m actually doing a fair amount of hex conversions and bit fiddling these days (webassembly bytecode) so it was actually something useful.
It's right there in Calculator's menu: View -> Programmer