65

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)?

chqrlie
  • 131,814
  • 10
  • 121
  • 189
Yaseen
  • 1,348
  • 2
  • 16
  • 31
  • http://stackoverflow.com/q/2527086/3049655 – Spikatrix Feb 20 '16 at 12:49
  • 2
    @CoolGuy: That is not a dup. There is a reason the argument in the linked question is converted to a boolean value. Here it is useless, as the value itself is not processed. – too honest for this site Feb 20 '16 at 13:12
  • @Olaf No, `__builtin_expect(x, 0)` and `__builtin_expect(!!(x),0)` should behave equally, hence it is useless there, too – Ctx Feb 20 '16 at 16:54
  • 4
    Of course, none of the answers presented here so far provide any insight into why `if (!!((ioValue & IO_DATA_BUZZER))) {` would contain _double_ parens around the bitwise-and. Things like that make me wonder if we aren't just seeing code that's suffered cut-and-paste / search-and-replace damage. – FeRD Feb 20 '16 at 18:54

5 Answers5

80

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.

fuz
  • 88,405
  • 25
  • 200
  • 352
  • 8
    Agreed. It is very uncommon in embedded programming, too. I suspect that is some relic from unrolling e.g. a `BITTEST` macro which is supposed to return the value of a bit, not only the set/clear information for further processing. Not such code is often written by students, etc. which are themselves not very experienced (and not very well payed), so they tend to use existing patterns and don't optimise/enhance well. – too honest for this site Feb 20 '16 at 13:20
  • 1
    @Olaf FWIW I've already seen code like that written by experienced devs. In Java :O – GOTO 0 Feb 20 '16 at 13:38
  • 5
    @GOTO0: Which **might** make them _experienced Java devs_, but not _experienced C devs_. – too honest for this site Feb 20 '16 at 13:45
  • 1
    Does the C spec actually mandate that `!0 == 1`, or is it just a minor detail that all contemporary implementations happen to hold? In the latter case, I'd expect a modern C compiler to toss the double negation out entirely. – John Dvorak Feb 20 '16 at 13:49
  • 7
    @Olaf As Java is a strongly typed language, there is no other operand type than booleans that logical negation can be applied to, making double negation useless. The only possible reason I can think of might be to force a NPE when applied to boxed booleans. – John Dvorak Feb 20 '16 at 13:55
  • 1
    @JanDvorak: That does not even matter here! But yes, a logical operator yields an `int` with either `0` or `1`, as my comment actually implies. – too honest for this site Feb 20 '16 at 14:01
  • 1
    @Olaf The only point I was trying to make is that it's not always possible to infer the degree of experience of a programmer from the code that they write. – GOTO 0 Feb 20 '16 at 14:12
  • I have seen the `!!` being used in competitive programming, for reading input. (will try to find a code snippet) – jermenkoo Feb 20 '16 at 15:48
  • 3
    Some compilers warn about constructs like `if (a = b)`, since these are likely to be erroneous. You could avoid the warning with `if ((a = b) != 0)`, but that is ugly as sin. `if (!!(a = b))` is *somewhat* more concise. – EOF Feb 20 '16 at 16:05
  • 3
    @EOF Usually, compilers pick up `if ((a = b))`. I think this convention dates back to lint. – fuz Feb 20 '16 at 19:05
  • 1
    @GOTO0: Sure, but it _is_ possible to infer the coding abilities of a programmer from the code they write. Being an "experienced" programmer is a meaningless qualification, there are far too many **terrible** programmers with years of experience. – BlueRaja - Danny Pflughoeft Feb 20 '16 at 19:49
  • @BlueRaja-DannyPflughoeft I was talking of people with 15+ years working experience writing nonsense like `if (!!(...))`. There's no reason to assume that only underpaid students could produce poor code. – GOTO 0 Feb 20 '16 at 22:17
  • I'd say it's less of a Java thing and more of a Java*Script* thing. Also think I've seen that in Python. But in no variety of C/C++/C# etc. or Java is it in any way helpful that I am aware of. – Darrel Hoffman Feb 21 '16 at 20:53
  • 1
    @DarrelHoffman you probably haven't seen it python, since python has no `!` operator, it uses `not`. – porglezomp Feb 22 '16 at 00:10
  • @porglezomp: Oops, maybe I was thinking of PHP? Perl? One of the P-languages anyhow. I don't use them much. Definitely seen this in JavaScript though. It's so common almost acceptable there. – Darrel Hoffman Feb 22 '16 at 14:38
55

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.

Damian Yerrick
  • 4,602
  • 2
  • 26
  • 64
16

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".

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • But... the control expression of an `if` statement isn't implicitly converted. Perhaps you are confusing C with C++? – fuz Feb 21 '16 at 15:10
4

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)));
technosaurus
  • 7,676
  • 1
  • 30
  • 52
  • You didn't mention explicitly, but the reason `!!` is present in `PIN_setOutputValue(hGpioPin, Board_LED1,!!(ioValue & IO_DATA_LED1));` is because the function will be declared to take some sort of integer type parameter (maybe `int`, or `unsigned char` for example) but if the flag has a large value, then even if the test succeeds the result may be converted to `0` – M.M Feb 25 '16 at 00:37
  • @M.M good point, I guess that isn't obvious. I'm just used to platforms with `typedef char bool;`, so didn't even think to mention that – technosaurus Feb 25 '16 at 00:58
3

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.

Community
  • 1
  • 1
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256