51

I want the iterator variable in a for loop to reverse iterate to 0 as an unsigned int, and I cannot think of a similar comparison to i > -1, as you would do if it was a signed int.

for (unsigned int i = 10; i <= 10; --i) { ... }

But this seems very unclear, as it is relying on the numerical overflow of the unsigned integer to be above 10.

Maybe I just don't have a clear head, but whats a better way to do this...

Disclaimer: this is just a simple use case, the upper limit of 10 is trivial, it could be anything, and i must be an unsigned int.

hiddensunset4
  • 5,825
  • 3
  • 39
  • 61

14 Answers14

51

You can use

for( unsigned int j = n; j-- > 0; ) { /*...*/ }

It iterates from n-1 down to 0.

Serge Dundich
  • 4,221
  • 2
  • 21
  • 16
28

The following does what you want:

for (unsigned i = 10; i != static_cast<unsigned>(-1); --i)
{
    // ...
}

This is perfectly defined and actually works. Arithmetic on signed types is accurately defined by the standard. Indeed:

From 4.7/2 (regarding casting to an unsigned type):

If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2^n where n is the number of bits used to represent the unsigned type)

and 3.9.1/4

Unsigned integers, declared unsigned, shall obey the laws of arithmetic modulo 2^n where n is the number of bits in the value representation of that particular size of integer

Alexandre C.
  • 55,948
  • 11
  • 128
  • 197
  • An unsigned variable cannot be < 0, and I don't want a compiler warning every time... – hiddensunset4 Mar 28 '11 at 11:24
  • I usually use this with a cast. Or I simply use an int in this case. – Alexandre C. Mar 28 '11 at 11:25
  • 1
    Why the downvote ? 4.7/2 seems clear on this point, the behavior of the code is perfectly defined and does what you want. – Alexandre C. Mar 28 '11 at 11:27
  • 1
    +1, answered the question; and although it covers all bases; wasn't quite what I was looking for in terms of an answer. A cast just seemed like cheating. – hiddensunset4 Mar 28 '11 at 11:38
  • 4
    @Daniel: if the cast is not present, `i` will get promoted to an int and the behavior is undefined. You *have* to cast the -1. So my initial post was incorrect. – Alexandre C. Mar 28 '11 at 11:43
  • @AlexandreC., promotion should always be signed->unsigned, not the other way around, so the cast does not need to be there. In fact it is better if it *isn't* there, because if someone changed the first `unsigned` to `unsigned long`, but forgot to change the later, then with the cast the promotion will be `signed->unsigned->unsigned long`, resulting in 0x00000000ffffffff on 64-bits, which is the wrong thing, but without the cast the promotion will be `signed->signed long->unsigned long`, resulting in the correct value. – Jan Hudec Sep 23 '21 at 09:23
4

My pattern for this is usually...

for( unsigned int i_plus_one = n; i_plus_one > 0; --i_plus_one )
{
    const unsigned int i = i_plus_one - 1;
    // ...
}
Adam Bowen
  • 10,820
  • 6
  • 36
  • 41
3

Are you really iterating down from some number greater than std::numeric_limits<int>::max()? If not, I would actually suggest just using a normal int as your loop variable and static_cast it to unsigned in the places in your code that expect it to be unsigned. This way you can use the intuitive >= 0 or > -1 condition and in general I would expect it to be more readable than any of the unsigned alternatives.

The static_cast would just be to tell the compiler how to operate on the variable and have no performance implications at all.

Mark B
  • 95,107
  • 10
  • 109
  • 188
  • Have you any proof to show that there is no actual cast being performed here? It would make sense, but I'd just like to make sure. Namely because I'm pumping numbers as fast as possible, and whilst a cast here there isn't critical, I don't see why I should if other (non casted) methods exist. – hiddensunset4 Mar 28 '11 at 13:51
3

Avoiding underflow

unsigned int i = n;
while (i != 0) {
  --i;
  ...
}
hiddensunset4
  • 5,825
  • 3
  • 39
  • 61
halcon_cxx
  • 31
  • 2
2

I can think the two options are either cast or singed numbers (can be done implicitly be comparing to -1, for example) or use the loop condition to check for overflow like this:

for(unsigned i=10;i>i-1;--i){ } // i = 10, 9, ... , 1
for(unsigned i=10;i+1>i;--i){ } // i = 10, 9, ... , 1,0

This loop will continue until i overflows (meaning that it reached zero). Note that is important that i iterates by 1, or you might end-up with an infinite loop.

Noam Weiss
  • 211
  • 1
  • 3
  • 2
    I originally used for(unsigned i = 10;i + 1 > 0;--i) { ... }, but it seemed a bit unclear... guess its not a simple solution :P. – hiddensunset4 Mar 28 '11 at 11:40
1

I would use two variables:

unsigned int start = 10;
for (unsigned int j = 0, i = start; j <= start; ++ j, -- i) {
    // ...
}

You can also use a while loop:

unsigned int start = 10;
unsigned int i = start + 1;
while (i --) {
    // ...
}
Sylvain Defresne
  • 42,429
  • 12
  • 75
  • 85
  • This was what I wanted to avoid, but may be the most 'obvious' solution given the circumstances. – hiddensunset4 Mar 28 '11 at 11:29
  • why use 2 variables if 1 is completely enough? To me there is no readability advantage. The second loop is good (mostly the same as mine except that iterator-index scope is out of the loop block). – Serge Dundich Mar 29 '11 at 09:02
  • Because I prefer the second version (I always make mistakes when writing the first version), but some people find it harder to understand. – Sylvain Defresne Mar 29 '11 at 09:17
1

Here is a simple trick to ovoid overflow if i iterates by 1:

for(unsigned int i = n-1; i+1 >= 1; i--) {;}

If you want i to iterate for more than 1:

unsigned int d = 2;
for(unsigned int i = n-1; i+d >= d; i-=d) {;}
hmofrad
  • 1,784
  • 2
  • 22
  • 28
1
for (unsigned int i = 10; i <= 10; --i)

You got it in one.

I don't want to give your decade old question back to you as an answer, but trying to avoid the underflow is going to obfuscate what you're trying to do ultimately.

Underflow for an unsigned integer is a well defined behavior that you can depend on, and expect other programmers to understand. Any work-around is going to make the range you're trying to act on harder to parse, and will likely be just as confusing to a beginner while imparting a poor example if they should pick it up as a "lesson" in reverse for loops

Anne Quinn
  • 12,609
  • 8
  • 54
  • 101
0
for(unsigned i = x ; i != 0 ; i--){ ...

And if you want to execute the loop body when i == 0 and stop after that. Just start with i = x+1;

BTW, why i must be unsigned ?

BenjaminB
  • 1,809
  • 3
  • 18
  • 32
0

I am new to c++, my answer might be realy stupid but, i do this for this kind of reverse loops;

size_t count = 3;
size_t newCount = 0;
if (count > 0)
    for (size_t i = count - 1; i >= newCount; i--)
    {
        //do your work here

        //and at the end
        if (i == 0)
            break;
    }

and it works. since "i--" part at the loop executed at the next step, break should work allright. what do you think is this way safe ?

Ibrahim Ozdemir
  • 613
  • 1
  • 5
  • 18
0

You can try and define the following macro:

#define for_range(_type, _param, _A1, _B1) \
    for (_type _param = _A1, _finish = _B1,\
    _step = static_cast<_type>(2*(((int)_finish)>(int)_param)-1),\
    _stop = static_cast<_type>(((int)_finish)+(int)_step); _param != _stop; \
_param = static_cast<_type>(((int)_param)+(int)_step))

Now you can use it:

for_range (unsigned, i, 10,0)
{
    cout << "backwards i: " << i << endl;
}

It can be used to iterate backwards and forwards through unsigned, integers, enums and chars:

for_range (char, c, 'z','a')
{
    cout << c << endl;
}

enum Count { zero, one, two, three }; 

for_range (Count, c, zero, three)
{
    cout << "forward: " << c << endl;
}

Despite its awkward definition it is optimized very well. I looked at disassembler in VC++. The code is extremely efficient. Don't be put off but the three for statements: the compiler will produce only one loop after optimization! You can even define enclosed loops:

unsigned p[4][5];

for_range (Count, i, zero,three)
    for_range(unsigned int, j, 4, 0)
    {   
        p[i][j] = static_cast<unsigned>(i)+j;
    }

You obviously cannot iterate through enumerated types with gaps.

Mikhail Semenov
  • 953
  • 8
  • 8
-1

One more way:

for(unsigned i = n-1; i < n ; --i) 
{       
    // Iterates from n-1 to 0
}

Simillarly for size_t (unsigned integer type) use the same trick

for(std::size_t i = n-1; i < n ; --i) 
{       
    // Iterates from n-1 to 0
}
SridharKritha
  • 8,481
  • 2
  • 52
  • 43
  • This is the same method as in the question itself. OP was looking for something else. – VLL Oct 26 '18 at 06:15
-1

Just:

int start = 10;
for(unsigned int iPlus1 = start + 1 ; iPlus1 > 0 ; iPlus1--) {
  // use iPlus1 - 1 if you need (say) an array index
  a[iPlus1 - 1] = 123; // ...
}

No?

Ash
  • 1,266
  • 4
  • 14
  • 24