I am attempting to explore aspects of bitwise math to learn more. In this situation I am just trying to use the left bitshift command to loop and print each iteration of a single bit shift. However, I am not achieving what I try and wonder if it is because I misunderstand something fundamental.
In the first step, I tried shifting a five bit number of type "byte", and terminating the loop right after. Instead, the loop runs to the full length of byte (eight bits). Does the logic treat the missing three bits as there but just undisplayed?
byte z;
void setup() {
Serial.begin(9600);
}
void loop() {
for ( z = 0b00001; z > 0b0; z <<= 1){
Serial.println(z,BIN);
delay(1000);
}
}
To test that idea out, I changed the conditional of the for loop to
z > 0b0100
which I'd expect to terminate the loop in two cycle, but instead nothing at all occurs.
Where are the fundamental flaws in my understanding?
I searched the forum and online pretty rigorously and cant seem to identify my fault.
You misunderstanding seems to be thinking that the internal representation of a number has anything to do with how you typed it in the assignment statement. z = 1, z = 0001, z = 0x1, and z = 0b0000001 all result in the same actual value in the variable.
So the byte always has 8 bits.
You could do:
for (z = 1; z < (1<<5); z <<= 1)
{
Serial.println(z, BIN);
delay(1000);
}
Yes, a byte is 8 bits always, integer types aren't stretchy like a string, they are rigid, mapping directly onto the chip's hardware registers and memory locations.
I'd prefer
for (unsigned z = 1 ; z <= 0b10000 ; z <<= 1)
Using unsigned rather than byte allows the compiler to choose the "natural" size of integer which may be more efficient (though not always). "unsigned" means the same as "unsigned int".
Try to always declare a variable at the point it is used - you declared z globally, but it seems only to be relevant to the loop, so its best to declare it in the loop.
in C++ types smaller than int (like uint8_t) are promoted to int before any arithmetic or bitwise operation.
So if I were to write the loop for (uint8_t z = 1; z <= 0b10000; z <<= 1),
the operation z <<= 1 is performed with z promoted to int, then the result is converted back to uint8_t before being stored in z.
This means the expression z <<= 1 is not done directly on an 8-bit type but on an int (16 or 32 bits depending on our common platforms), then truncated to 8 bits.
If we use your unsigned z, the promotion has no effect since z is already an int size.
The question is more how you use z afterwards in the loop, having more than 8 bits might or might not be an issue esp. if you want to use it as a mask (although here z <= 0b10000 will ensure you are always with a null MSB)
The operation is preformed AS IF arguments were promoted to int/uint.
Even at -O0, the compiler is smart enough to realize that it doesn't actually have to do the promotion...
It does better at -Os, except that it replaces the comparison of z with a counted loop (using an int!) (an "optimization" I find particularly annoying (uses registers!) and unnecessary):