2

I'm reading an open code in C which uses glib, and I found something like this

gboolean function()
{
  guint myVar = 0;
  myVar = (!!globalVar1 + !!globalVar2 + !!globalVar3);
  return !!myVar;
}

I don't understand what's exactly happening with that double exclamation mark.

phuclv
  • 37,963
  • 15
  • 156
  • 475
ad_valle
  • 65
  • 4
  • 2
    `!` is a logical `not`. `!!` is logical `not` applied twice. This is effectively converting any non-zero value into `1` – Eugene Sh. Dec 03 '21 at 15:38
  • 1
    Does this answer your question? [What does !! (bang bang) mean?](https://stackoverflow.com/questions/61274027/what-does-bang-bang-mean) – phuclv Dec 03 '21 at 15:52
  • [What is "!!" in C?](https://stackoverflow.com/q/14751973/995714), [!! c operator, is a two NOT?](https://stackoverflow.com/q/10307281/995714) – phuclv Dec 03 '21 at 15:53
  • What is `gboolean function()`? Even in the 80s it would have been considered bad practice to name a function "function". But it's not the 80s, and you should declare the parameters. – William Pursell Dec 03 '21 at 15:53
  • I've always called this "Boolean reduction" – Steve Friedl Dec 03 '21 at 16:06

4 Answers4

4

The unary ! operator performs a logical NOT operation. If its operand is non-zero, it evaluates to 0. If its operand is 0, it evaluates to 1.

When two of them are put together like this, it normalizes the operand to a boolean value. So if the operand is 0 the result is 0 and if the operand is non-zero the result is 1.

In the context of the larger expression:

myVar  = (!!globalVar1 + !!globalVar2 + !!globalVar3);

This will set myVar to a value between 0 and 3. Then this:

return !!myVar;

Normalizes that value to 0 or 1. So the end result is that 1 is returned if any of the 3 variables are non-zero, otherwise 0 is returned.

The body of the function can be rewritten as:

return globalVar1 || globalVar2  || globalVar3;

Which more clearly expresses the intent. The || operator does involve branching however, so the code as written was probably trying to avoid that branching.

Generally though, compilers are pretty good at optimizing, so such micro-optimizations are not really necessary.

dbush
  • 205,898
  • 23
  • 218
  • 273
1

Let's at first consider this statement

myVar = (!!globalVar1 + !!globalVar2 + !!globalVar3);

Now according to the C Standard (6.5.3.3 Unary arithmetic operators)

5 The result of the logical negation operator ! is 0 if the value of its operand compares unequal to 0, 1 if the value of its operand compares equal to 0. The result has type int. The expression !E is equivalent to (0==E)

For example If you have a variable like this

int x = 10;

then applying the operator ! to the variable !x you will get 0. Applying the operator the second time !!x you will get 1. It is the same if to write x != 0.

So the result of the assignment is a non-zero value if at least one of the operands, globalVar1, globalVar2, and globalVar3. is not equal to 0.

The above statement can be rewritten the following way

myVar = ( ( globalVar1 != 0 ) + ( globalVar2 != 0 ) + ( globalVar3 != 0 ) );

The result of the assignment can be either 0 (if all operands are equal to 0), or 1 (if only one operand is not equal to 0), or 2 ( if two operands equal to 0), or 3 (if all operands are equal to 0).

The function need to return 1 if at least one operand is not equal to 0 or 1 otherwise.

You could just write in the return statement

return myVar != 0;

But the author of the code decided to write

return !!myVar;

It seems he likes very much the negation operator !.:)

The purpose of this "balancing act" with the negation operator is to return exactly either 0 or 1.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
1

!!val gives 0 if val is zero or 1 if val is not zero.

Example usage:

//function counting non zero elelemnts of array
size_t countNonZero(const int *array, size_t size)
{
    size_t count = 0;
    while(size--)
        count += !!*array++;
    return count;
}

In your example, there is no need of the !! operator as in C any non zero value is considered as the true. Simple || operator should be used.

that function should be rewritten as

gboolean function()
{
  return globalVar1 || globalVar2 || globalVar3;
}
0___________
  • 60,014
  • 4
  • 34
  • 74
0

It's the same as the single exclamation mark, twice. It's negating a value twice.

This has the effect of turning all integers to either 0 (if they were already 0) or 1 (if they were any non-zero value)

Alexander
  • 59,041
  • 12
  • 98
  • 151