Using if (!!(expr)) instead of if (expr)

CExpression

C Problem Overview


While reading the example code provided by Texas Instruments for their SensorTag I came across the following snippet.

void SensorTagIO_processCharChangeEvt(uint8_t paramID) { 
    ...

    if (!!(ioValue & IO_DATA_LED1)) {
        PIN_setOutputValue(hGpioPin, Board_LED1, Board_LED_ON);
    } else {
        PIN_setOutputValue(hGpioPin, Board_LED1, Board_LED_OFF);
    }
    
    if (!!(ioValue & IO_DATA_LED2)) {
        PIN_setOutputValue(hGpioPin, Board_LED2, Board_LED_ON);
    } else {
        PIN_setOutputValue(hGpioPin, Board_LED2, Board_LED_OFF);
    }
    
    if (!!((ioValue & IO_DATA_BUZZER))) {
        Clock_start(buzzClockHandle);
    }
    ...
}

The declaration is like this (in the same file).

#define IO_DATA_LED1   0x01
static uint8_t ioValue;

Does if (!!(ioValue & IO_DATA_LED1)) offer any advantage over if (ioValue & IO_DATA_LED1)?

C Solutions


Solution 1 - C

Applying the logical not (!) operator twice has the purpose of normalising a value to be either 0 or 1. In a control expression of an if-statement, that doesn't make any difference. The if-statement only cares about the value being zero or nonzero, the little !! dance is completely useless.

Some coding style guides might mandate this kind of dance, which could be the reason why the TI code you posted does it. I haven't seen any that do so though.

Solution 2 - C

The expression !!x, or !(!x), means 1 if x is a true value (a nonzero number or a nonnull pointer), otherwise 0. It is equivalent to x != 0, and it's nearly the same as C99 (_Bool)x but available in compilers that predate C99 or whose developers have chosen not to implement C99 (such as cc65 which targets the MOS 6502).

The conditional as a whole is equivalent to the following:

if (ioValue & IO_DATA_LED1) {
    /* what to do if the IO_DATA_LED1 bit is true */
} else {
    /* what to do if the IO_DATA_LED1 bit is false */
}

In C, it means "if the bitwise AND of those two values is nonzero, execute the block."

But some coding style guides might prohibit a bitwise AND (&) at the top level of an if statement's condition, assuming it to be a typo for the logical AND (&&). It's in the same class of mistakes as using = (assignment) instead of == (equality comparison), for which many compilers offer a diagnostic. GCC Warning Options describes diagnostics like these:

> -Wlogical-op: Warn about suspicious uses of logical operators in expressions. This includes using logical operators in contexts where a bit-wise operator is likely to be expected. > > -Wparentheses: Warn if parentheses are omitted in certain contexts, such as when there is an assignment in a context where a truth value is expected

Use of a paraphrase such as (a & B) != 0, (_Bool)(a & B), or !!(a & B) communicates to the compiler and to other developers that use of a bitwise operator was intentional.

See also a related answer about !!x in JavaScript.

Solution 3 - C

In MSVC converting an integer to a bool implicitly in an if statement can generate a warning. Doing so via !! does not. Similar warning may exist in other compilers.

So supposing the code was compiled with that warning enabled, and a decision to treat all warning as errors, using !! is a short and portable way to say "yes, I want this integer to be a bool".

Solution 4 - C

Although silencing the compiler warning for the bit-wise & is the most likely, this looks like it could also be the result of a refactoring to add enums for readability from:

PIN_setOutputValue(int,int,bool); //function definition
PIN_setOutputValue(hGpioPin, Board_LED1,!!(ioValue & IO_DATA_LED1));
PIN_setOutputValue(hGpioPin, Board_LED2,!!(ioValue & IO_DATA_LED2));
//note: the !! is necessary here in case sizeof ioValue > sizeof bool
//otherwise it may only catch the 1st 8 LED statuses as @M.M points out

to:

enum led_enum {
  Board_LED_OFF = false,
  Board_LED_ON = true
};
PIN_setOutputValue(int,int,bool); //function definition
//...
PIN_setOutputValue(hGpioPin, Board_LED1,!!(ioValue & IO_DATA_LED1)?Board_LED_ON:Board_LED_OFF);
PIN_setOutputValue(hGpioPin, Board_LED2,!!(ioValue & IO_DATA_LED2)?Board_LED_ON:Board_LED_OFF);

Since that exceeded the 80 character limit it was then refactored to

if (!!(ioValue & IO_DATA_LED1)) {
    PIN_setOutputValue(hGpioPin, Board_LED1, Board_LED_ON);
} else {
    PIN_setOutputValue(hGpioPin, Board_LED1, Board_LED_OFF);
}

if (!!(ioValue & IO_DATA_LED2)) {
    PIN_setOutputValue(hGpioPin, Board_LED2, Board_LED_ON);
} else {
    PIN_setOutputValue(hGpioPin, Board_LED2, Board_LED_OFF);
}

Personally I would have preferred the initial version for readability, but this version is common when lines of code are used as a metric (I'm surprised it didn't declare variables for each state, set each state separately and then use that).

The next version of this "Best Practice" code might look like:

bool boardled1State;
bool boardled2State;
//...

boardled1State = !!(ioValue & IO_DATA_LED1);
boardled2State = !!(ioValue & IO_DATA_LED2);
//...

if (boardled1State) {
    PIN_setOutputValue(hGpioPin, Board_LED1, Board_LED_ON);
} else {
    PIN_setOutputValue(hGpioPin, Board_LED1, Board_LED_OFF);
}

if (boardled2State) {
    PIN_setOutputValue(hGpioPin, Board_LED2, Board_LED_ON);
} else {
    PIN_setOutputValue(hGpioPin, Board_LED2, Board_LED_OFF);
}
//... and so on

All of that could have been done like this:

for (int i=0;i<numleds;i++)
        PIN_setOutputValue(hGpioPin, i ,!!(ioValue & (1<<i)));

Solution 5 - C

> OP is looking at some old coding idiom - which made some sense BITD (back-in-the day).

  1. A primary use of !! was to handle C implementations that converted the expression in an if(expr) to int rather than testing against zero.

Consider what happens when expr is converted to int and then tested against 0. (Since C89, this is non-conforming as the test should be a direct test against 0)

int i;
long li;
double d;

// no problems
if (i & 5) ...
if (d > 4.0) ...

// problems
if (li & 0x10000) ...  (Hint: int is 16-bit)
if (d)                 (d might have a value outside `int` range.

// fix
if (!!(li & 0x10000))
if (!!d)

So on pre C89 compliers and non-conforming C89 and later, using !! coped with that weakness. Some old habits take a long time to die.

  1. In early C++, there was no bool type. So code that wanted to test for trustfulness needed to use the !! idiom

     class uint256;  // Very wide integer
     uint256 x;
    
     // problem  as (int)x may return just the lower bits of x
     if (x) 
    
     // fix
     if (!!x) 
    
  2. What happens with C++ today (I know this is a C question) when there is no (bool) operator defined, is not the (int) operator used? This results in the same problem as #2. As for many early years C and C++ code bases were kept in sync, using !! had relevance with constructs like if (!!x).


Using !! works today but has certainly fallen out of favor as it solves problem that no longer occurs with any significant frequency.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionYaseenView Question on Stackoverflow
Solution 1 - CfuzView Answer on Stackoverflow
Solution 2 - CDamian YerrickView Answer on Stackoverflow
Solution 3 - CYakk - Adam NevraumontView Answer on Stackoverflow
Solution 4 - CtechnosaurusView Answer on Stackoverflow
Solution 5 - Cchux - Reinstate MonicaView Answer on Stackoverflow