57

In many programming competitions I have seen people write this type of for-loop

for(i = 0; i < (1 << 7); i++)

Unless I am missing something, that's the same as

for(i = 0; i < 128; i++)

Why use the (1 << 7) version?
Isn't calculating the condition every time an unnecessary overhead?

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
harrythomas
  • 1,407
  • 1
  • 13
  • 17
  • 12
    C has a thing called "the as-if rule" (well - not exactly; C++ has that, and C has equivalent text but doesn't call it that specific name): what's important is that the program produces the same output as what your code outputs. ("output" includes accesses to volatile variables, and calls to library functions). Other than that, it can do what it likes. If you wrote a program to generate prime numbers, the compiler could detect that and just hardcode a list of prime numbers in the executable. In your code, all compilers would hardcode 128 rather than doing a shift at runtime. – M.M Aug 30 '14 at 12:16
  • 4
    Maybe not all people know the powers of 2 between 2^0 and 2^32 by heart. (Although, admittedly, I think that a programmer should at least know them up to 2^16, and some "interpolation points" like 2^20, 2^24, 2^30 and 2^32 itself...) – Marco13 Aug 30 '14 at 23:43
  • 1
    Using [godbolt](http://gcc.godbolt.org/) as I do in my answer can be a quick way to check assumptions about optimizations. It can be surprising how many old assumptions don't apply. – Shafik Yaghmour Aug 31 '14 at 01:35
  • 13
    @harrythomas: Writing `128` will make it look as a "magic constant" of unexplainable origin. Writing `(1 << 7)` might makes it immediately clear what that constant stands for and where it came from. It is preferable to do it that way. – AnT stands with Russia Aug 31 '14 at 01:46
  • 11
    Both versions are bad. It should be `for(i = 0; i < NUMBER_OF_STEPS; i++)`. Don't use magic numbers. – glampert Aug 31 '14 at 01:46
  • 1
    @MattMcNabb well both C99 and C11 have an index entry for `as-if rule` which points to `5.1.2.3` so they do have it they just don't use it in the main text. I guess they consider it self-evident. – Shafik Yaghmour Aug 31 '14 at 02:40
  • 2
    @glampert Unless the only thing that would achieve is another layer of indirection. – Luka Horvat Aug 31 '14 at 12:26
  • 5
    @LukaHorvat: Then write `for(i = 0; i < ONE_HUNDRED_TWENTY_EIGHT; i++)`. I call this a _magic constant_!. – rodrigo Aug 31 '14 at 19:36
  • @glampert: Whether the code should be written as `(1<<7)`, or `(1< – supercat Sep 01 '14 at 03:40
  • @rodrigo - I've actually worked on codebases with that sort of madness! `for (i=0; i – Echelon Sep 01 '14 at 11:34
  • Both named constants and raw numbers can be misused, like pretty much everything else in programming. There are a few rare cases, in my opinion, where a raw number is better than a named constant. For example, defining the sides of a triangle. Everyone knows it is 3, so there is no need to define a constant for that. But I think those cases are the exception. Also, I think we are falling a bit into the realm of personal taste here, so I have no further comments on that. – glampert Sep 01 '14 at 14:26
  • 2
    @glampert: IMO, NUMBER_OF_STEPS would be a poor choice; it is uninformative and obvious from the code. I'd prefer something context-related like ASCII_RANGE for instance. –  Sep 03 '14 at 06:38
  • @YvesDaoust, Of course. That's what I meant. If the constant doesn't convey any information than it is just as good as a magic number. – glampert Sep 03 '14 at 17:29

7 Answers7

76

Yes, they are equivalent in behavior.

Then why do people use the (1 << 7) version?

I guess, they use it to document it is a power of 2.

Calculating the condition every time must be an overhead! I am unable to find the reason behind this!

Not really, any normal compiler will replace 1 << 7 by 128 and so both loops will have the same performances.

(C11, 6.6p2) "A constant expression can be evaluated during translation rather than runtime, and accordingly may be used in any place that a constant may be."

ouah
  • 142,963
  • 15
  • 272
  • 331
  • 20
    Note that the C language *requires* the compiler to *be able* to evaluate constant expressions at compile-time, since they're usable in all sorts of contexts where the actual value must be known, either to determine constraint violations (e.g. negative array size or invalid bitfield widths) or expression types (due to the fact that whether an expression is a null pointer constant depends on the value, and the result can propagate in very powerful ways via the `?:` operator). Thus there's no good reason not to evaluate all constant expressions fully at compile-time. – R.. GitHub STOP HELPING ICE Aug 31 '14 at 05:37
  • Actually, I thought it was the preprocessor that always evaluates constant expressions at compile time. So that's a guarantee that there's zero overhead in using expressions - so long as they are more informative than a constant. – Echelon Sep 01 '14 at 11:37
  • 1
    @Echelon: the preprocessor is just able to recognize preprocessor tokens and perform string substitution. Besides that, it has no knowledge of the C/C++ syntax and semantics. –  Sep 03 '14 at 06:31
  • 1
    @YvesDaoust @Echelon Note that preprocessor constant expression is a different class of constant expression. Preprocessor arithmetic is done using the largest integer type in the target (`intmax_t` or `uintmax_t`). An `#if` controlling constant expression (e.g., in `#if 1 << 7 == 128`) *is required* to be evaluated at preprocessor time (more specifically at translation phase 4). – ouah Sep 03 '14 at 11:56
  • @ouah: you are right, the preprocessor has some knowledge of arithmetic expressions and my previous remark is wrong. –  Sep 03 '14 at 12:06
  • @YvesDaoust @ouah I agree the preprocessor, C, and C++ compilers will have different semantics around constant expression evaluation; but in my experience of writing compiler tools back in the day, expressions like `(1<<7)` never made it past the preprocessor without being evaluated. – Echelon Sep 04 '14 at 07:02
  • 1
    @Echelon: I just had a look at an intermediate preprocessor output (Visual Studio compiler), and no constant is substituted in the source code. Could a preprocessor perform valid literal constant substitution without knowing the whole language syntax ? –  Sep 04 '14 at 07:38
  • @YvesDaoust I stand completely corrected. I have just run a test too, using gcc, and neither constants nor #define macros are evaluated, unless - as you pointed out - the expression is required to be evaluated (`#if (1<<7) == 128`) in order to process the required preprocessor directives. Thank you for educating me! – Echelon Sep 04 '14 at 22:00
31

Let's translate each one of these options into plain English:

for(i = 0; i < (1 << 7); i++) // For every possible combination of 7 bits
for(i = 0; i < 128; i++)      // For every number between 0 and 127

Runtime behavior should be identical in both cases.

In fact, assuming a decent compiler, even the assembly code should be identical.

So the first option is essentially used just in order to "make a statement".

You could just as well use the second option and add a comment above.

barak manos
  • 29,648
  • 10
  • 62
  • 114
  • 1
    An even better statement would be a more appropriate name for `i`. `i` tends to mean something like "the index I'm working on", but the programmer is trying to say "the *pattern* I'm working on". Something like `bitCombo` would make it clear without the comment. Contests tend to emphasize being short and clever over clear though, so maybe they stuck with `i` for the mystique. – Mirinth Aug 31 '14 at 16:56
19

1 << 7 is a constant expression, the compiler treats it like 128, there's no overhead in run time.

Without the loop body, it's hard to say why the author uses it. Possibly it's a loop that iterates something associated with 7 bits, but that's just my guess.

Yu Hao
  • 119,891
  • 44
  • 235
  • 294
14

Then why do people use the (1 << 7) version?

It is a form of documentation, it is not a magic number but 2^7(two to the seventh power) which is meaningful to whomever wrote the code. A modern optimizing compiler should generate the exact same code for both examples and so there is no cost to using this form and there is a benefit of adding context.

Using godbolt we can verify this is indeed the case, at least for several versions of gcc, clang and icc. Using a simple example with side effects to ensure that the code is not totally optimized away:

#include <stdio.h>

void forLoopShift()
{
  for(int i = 0; i < (1 << 7); i++)
  {
    printf("%d ", i ) ;
  }
}

void forLoopNoShift()
{
  for(int i = 0; i < 128; i++)
  {
        printf("%d ", i ) ;
  }
}

For the relevant part of the code we can see they both generate the following see it live:

cmpl    $128, %ebx

What we have is an integer constant expression as defined in the draft C11 standard section 6.6 Constant expressions which says:

An integer constant expression117) shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants, sizeof expressions whose results are integer constants,[...]

and:

Constant expressions shall not contain assignment, increment, decrement, function-call, or comma operators, except when they are contained within a subexpression that is not evaluated.115)

and we can see that a constant expression is allowed to be evaluated during translation:

A constant expression can be evaluated during translation rather than runtime, and accordingly may be used in any place that a constant may be.

Michael Kohne
  • 11,888
  • 3
  • 47
  • 79
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
5

for(i = 0; i < (1 << 7); i++)

and

for(i = 0; i < 128; i++)

gives same performance but developer can take huge advantage in case for(i = 0; i < (1 << 7); i++) is used in a loop as

for(int k = 0; k < 8; k++)
{
  for(int i = 0; i < (1 << k); i++)
   {
    //your code
    }

}

Now it is in the inner loop upper limit i.e. (1 << k) changes with power of 2 runtime. But it is applicable if your algorithm requires this logic.

Anirban Pal
  • 529
  • 4
  • 10
  • 2
    `(1 << k)` is a [loop invariant](https://en.wikipedia.org/wiki/Loop_invariant) so the compiler *should* calculate it only once per *outer* loop iteration. In fact, it may perform [induction variable elimination](https://en.wikipedia.org/wiki/Induction_variable), converting this to `for (int j = 1; j <= 128; j <<= 1) for (i = 0; i < j; i++) { ... }` – zwol Aug 30 '14 at 13:59
3

The compiler outputs the same code for both cases. you probably want to use different forms depending on the context.

  1. You can use NUM_STEPS or NUM_ELEMENTS_IN_NETWORK_PACKET when it's a constant part or a design choice in your algorithm that you want to make clear.
  2. Or you can write 128, to make clear it's 128, a constant.
  3. Or write 1 << 7 if you're at a competition and the test said something like "run it 2^7 times".

Or, you can be showing off that you know bit operations!

In my humble opinion, programming is like writing a letter for two people, the compiler and the person that will have to read it. What you're meaning should be clear for both.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
Luc
  • 320
  • 4
  • 11
  • There was never a program written that was understood by a person and not a computer. A program should always be written readable for other (people). Computer will always understand it anyway (given - of course - it is valid syntax) – hashier Sep 04 '14 at 16:55
-3

It's evaluated by the preprocessor since both operands are constant.

But if you're going to use the number instead of the bit shift shouldn't it be 0x0100?

Chris Fox
  • 641
  • 6
  • 10