why is double negation useful in shiftOut for example?

Double negation is used for example within wiring_shift.c for shiftOut as below:

void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val)
{
uint8_t i;

for (i = 0; i < 8; i++) {
if (bitOrder == LSBFIRST)
digitalWrite(dataPin, !!(val & (1 << i)));
else
digitalWrite(dataPin, !!(val & (1 << (7 - i))));

digitalWrite(clockPin, HIGH);
digitalWrite(clockPin, LOW);
}
}

Elsewhere it has been explained that the !! double negation ensures that a 0 returns a 0 and any non-zero value returns a 1. That makes sense, but wouldn't the Bitwise AND function do that if we AND with a 1 anyway? What benefit does the extra double negation give in this situation?

It places the logical value in bit zero, which is where HIGH and LOW are currently defined.

Which is unnecessary, because in digitalWrite() it only tests if the value passed is equal to LOW (ie, 0)...

	if (val == LOW) {
		*out &= ~bit;
	} else {
		*out |= bit;
	}

Ahh, That makes perfect sense, no matter what the outcome of the bitwise AND was, there will always be a 1 in Bit 0, which is the bit we are sending to the pin

Thanks and big ups for a perfect explanation. While in the particular case we are only testing for low, it is resilient for the more general case.

And which is somewhat semantically flawed as sending true or false should not be considered (if you are a purist) the same as sending HIGH or LOW

It works because if the operand is not bool, it is converted to bool using contextual conversion (standard C) and by (relatively) arbitrary convention and luck because the authors have selected the same value as true and false for HIGH and LOW)

If you care, this should be written as condition ? HIGH : LOW.

J-M-L:
and luck because the authors have selected the same value as true and false for HIGH and LOW)

I consider it more good design than luck.

J-M-L:
If you care, this should be written as condition ? HIGH : LOW.

Of course that causes the compiler to generate extra code to translate any non-zero value to one. Fortunately for me I'm not a purist and can understand:

boolean inputState = !digitalRead(InputPin);
digitalWrite(OutputPin, !inputState);

A purist would have to write:

int inputState = digitalRead(InputPin) == HIGH ? LOW : HIGH;
digitalWrite(OutputPin, inputState == LOW ? HIGH : LOW);

I wonder if the compiler is smart enough to generate the same code for both. I assume it will at least store a 'boolean' in a byte but an 'int' as two bytes, even though it is used for 1/0 values.

agreed - that's why few do so :slight_smile: but in some strongly typed languages that would bite you

fortunately we code in C/C++ :slight_smile:

This code fancifully fixes its own oversight - The effort to shift the bit to the LSB arises because it is not in the "right" position. This can be avoided by shifting the tested bit into the LSB instead, like:

digitalWrite(dataPin, (val >> i) & HIGH );