203

I almost never see a for loop like this:

for (int i = 0; 5 != i; ++i)
{}

Is there a technical reason to use > or < instead of != when incrementing by 1 in a for loop? Or this is more of a convention?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Zingam
  • 4,498
  • 6
  • 28
  • 48
  • 106
    It makes it more readable, plus, if you ever change `i++` to `i+=2` (for example), it will run for a very long time (or possibly forever). Now, since you typically use `<` for those cases where you increment the iterator by **more than** 1, you might as well use `<` also for the case where you increment it by 1 (for consistency). – barak manos Jul 16 '15 at 13:31
  • 113
    `5 != i` is evil – Slava Jul 16 '15 at 13:32
  • 1
    No technical reason. Just a question of style/preference. – Werner Henze Jul 16 '15 at 13:32
  • 9
    ... and robustness of the code. @WernerHenze – alk Jul 16 '15 at 13:41
  • 3
    @barakmanos I would argue that if you are making such a change later you should really know what you are doing and do it properly and check all checks and conditions. – Zingam Jul 16 '15 at 14:00
  • 4
    I agree with Zingam regarding the later change and being conscious of your actions, but I think @barakmanos makes a valid point. – dgo Jul 16 '15 at 14:09
  • 2
    != is necessary when iterating over some more complicated types, so people can be tempted to use it everywhere. On the other hand, IIRC openmp requires < to parallelize a loop. – Marc Glisse Jul 16 '15 at 14:33
  • 27
    You should always be conscious of what you're doing, but you will make mistakes anyway. Using < reduces the possibility of someone introducing a bug into this code. – Aurast Jul 16 '15 at 15:53
  • 6
    Your example uses int. If it used float you may hit one of the floating number that are almost but not quite == the other number (similar to 1.99999 == 2.0). < would guard against this kind of problems. But if the increment is always 1 then you are not likely to see this kind of problems either. Only if you increment is more complex you might run into that. – Ole Tange Jul 16 '15 at 16:01
  • 40
    @OleTange If you are iterating using floating point numbers you are already screwed.. – CaptainCodeman Jul 16 '15 at 16:27
  • 1
    `for` loop is pre-test, so there's not a lot of difference. I came across a piece of asm code that does post-test but still uses `!=` (yes, JNE), the very ugly thing is that, when trip count is 0, the first iteration still runs, loop counter is incremented so `!=0`, then condition is true for(almost)ever. – user3528438 Jul 16 '15 at 17:29
  • 29
    @Slava...not evil. You've apparently never been bit in C by a simple typo making the difference between `if ( i == 5 ) ...` and `if ( i = 5 )`. Very different things. Reversing the operands prevents the problem: since literals are not `lvals`, they can't be assigned to and the compiler hurls on the typo. @Zingam: good defensive programming! – Nicholas Carey Jul 16 '15 at 17:38
  • 5
    @NicholasCarey I've been hit with that problem. Issue is you try to prevent a bug that is easy to catch and may produce bugs that are hard to catch. – Slava Jul 16 '15 at 20:46
  • 4
    As a programmer for 50 years (started with FORTRAN) I can say that using `<` helps prevent a lot of problems, in part because it makes you think a little more about what you're coding. Yes, it can hide some problems as well, but it's a worthwhile tradeoff. – Hot Licks Jul 17 '15 at 20:01
  • 1
    @CaptainCodeman Actually, a simple integer loop like this _should_ still work with a float variable. Not that I'd recommend it! (Really, for floats, equality is just for comparing a variable with its prior value to see if it has been touched) – pyrocrasty Jul 18 '15 at 21:07
  • 2
    @NicholasCarey I have personally *always* been saved by a compiler warning in those cases. – Antonio Jul 20 '15 at 06:57
  • @OleTange You (almost) **never** use a floating point variable as a loop counter for the very reason you mention – floating point variables are inexact for many values and the errors will accumulate. – Ian Goldby Jul 20 '15 at 10:04
  • Many years ago in Mainframe days using PL/1 with CICS I fell for this with a loop. Checking that the loop counter wasn't equal to 17, and if not incrementing the counter. But a bug elsewhere meant very occasionally the loop started at 18, so the loop carried on for ages. As the counter was used as a subscript for an array it was updating it carried on writing to storage, until it hit something important (the system table listing the entry points for various programs) and brought CICS down. – Kickstart Jul 20 '15 at 11:47
  • @Slava - what is evil about `5 == i` ? – Toni Leigh Jul 20 '15 at 13:38
  • @ToniLeigh in short: `5.operator==( i )` fails. It is even worse if `i` has implicit conversion operator. – Slava Jul 20 '15 at 16:40
  • @Slava - I must say I've never seen `5.operator==( i )` in any language I've used, how is it related to `5 == i` ? – Toni Leigh Jul 20 '15 at 16:43
  • 1
    @ToniLeigh if `i` is an instance of class that has `operator==(int)` then statement `i == 5` would compile, but `5 == i` would fail. If `i` has `operator double()` then `5.0 == i` would produce difficult to catch bug. – Slava Jul 20 '15 at 16:52
  • @Slava fair enough, but this case sounds somewhat unusual, perhaps in the context given it's evil, but generally it's a good catch for something equally pernicious - the accidental assignment of 5 to `i` resulting from the typo `i = 5` – Toni Leigh Jul 20 '15 at 17:06
  • 1
    @ToniLeigh main problem that `i = 5` is easy to catch and IMHO all modern compilers generate warning for it, case like implicit double would not have any warnings and difficult to catch - it may work in some cases. Irony is main reason OP asked this question because `5 > i` would be even worse, so he wants to keep constant on the left and that implies `!=` instead of < or > – Slava Jul 20 '15 at 17:14
  • 4
    `5 != i` that a yoda condition is – eversor Jul 22 '15 at 07:06
  • yes.. reversing the operands is quite useful.. literals are not lvals so if you make a typo and write 5 = i you will get a syntax error. YODA conditions helpful are!!!!!!!!!!!! – Gianluca Ghettini Jul 24 '15 at 10:25
  • Yoda talk unfamiliar to me. Yoda code me no like. Idiomatic, good is, no hate code 2 years later when idiomatic it is. Like everything else, it looks. Testing care takes, of rest. –  Nov 20 '15 at 17:54
  • I'd argue that this is not a duplicate because: (1) the other question is asking specifically about efficiency and this one is open to technical reasons for doing or not doing this, and (2) the other question is asking about a broader swath of languages and thus is less likely to deal with language-specific issues (like C++ iterators). – Adrian McCarthy Dec 18 '15 at 19:59
  • @Slava Yesterday VC++ 2015 didn't warn me about if (variable = constant) :) I have a difficulty understanding your example why 5 == i that evil. If I ask a question - would you mind to elaborate more thoroughly? – Zingam Jan 21 '16 at 10:27
  • @Zingam I am not sure if such question would be applicable to SO as it answer could be opinion based. – Slava Jan 21 '16 at 14:42
  • Unfortunately, the "duplicate" question also conflates a second issue (pre- v. post-increment) and focuses on the efficiency of that. – Adrian McCarthy Jan 27 '16 at 23:18
  • The duplicate is a bad question in general, it brings in all manner of different languages. C++ specifically has the issue of operator overloading - a reason to pick one operator over the other might be to reduce the amount of operators that you need to overload. I'll re-open this. – Lundin Sep 05 '17 at 08:47
  • Re floats: on all sensible implementations, floats represent integers exactly until you overflow the range of mantissa. On most platforms where `sizeof(double) > sizeof(int)`, a `double` can hold integers larger than what fits into an `int`. – Kuba hasn't forgotten Monica Oct 16 '18 at 03:01

21 Answers21

309
while (time != 6:30pm) {
    Work();
}

It is 6:31pm... Damn, now my next chance to go home is tomorrow! :)

This to show that the stronger restriction mitigates risks and is probably more intuitive to understand.

jpaugh
  • 6,634
  • 4
  • 38
  • 90
Antonio
  • 19,451
  • 13
  • 99
  • 197
  • 25
    Sometimes, attempting to "mitigate risks" just ends up hiding the bug. If the design of the loop should preclude the 6:30 p.m. from slipping by unnoticed, then using `<` will hide the bug but using `!=` would make it obvious that there's a problem. – Adrian McCarthy Jul 16 '15 at 17:09
  • 29
    @AdrianMcCarthy: Not necessarily; it could just introduce a second bug, making diagnosis _harder_. – Lightness Races in Orbit Jul 16 '15 at 17:20
  • @LightnessRacesinOrbit: I've yet to see an instance of that. Remember, I said, "If the design of the loop should preclude the 6:30 p.m. from slipping by unnoticed...." – Adrian McCarthy Jul 16 '15 at 17:27
  • 4
    @AdrianMcCarthy: That's kind of my point; you could have a few instances of it that you haven't seen yet ;) – Lightness Races in Orbit Jul 16 '15 at 17:29
  • 6
    Iterators are a perfect example of @AdrianMcCarthy point I think. The recommended comparison for them is != instead of <. – Taekahn Jul 17 '15 at 03:45
  • 1
    I like the intuition given in this answer. However, it doesn't really focus the question itself. (technical reasons vs. conventions) – Num Lock Jul 17 '15 at 07:23
  • If the design of the loop should preclude the 6:30 pm from slipping by unnoticed then one should explicitly check that it has been seen/it is the time upon completion of the loop... – eggyal Jul 18 '15 at 07:57
  • 5
    I've seen exactly this error (and it caused 3 months of debugging) - but this answer entirely misses the point. In the given example it is **impossible** for the end condition to be missed. You could even say that to consciously choose != over < is to make a strong assertion of this fact. Mitigating a risk that definitively doesn't exist is a code smell to me. (Having said that, you can equally argue that < is idiomatic, that that is as good a reason to use it as any.) – Ian Goldby Jul 20 '15 at 10:09
  • 1
    If someone updates the loop counter within the for loop then it could skip the value 5 – Kickstart Jul 20 '15 at 15:41
  • 1
    @ian-goldby, "_...In the given example it is *impossible* for the end condition to be missed_": that depends on how long `work()` takes... – Paul Ogilvie Jul 22 '15 at 19:53
  • 2
    @PaulOgilvie I think Ian Goldby referred to the example given in the question, not in my answer. – Antonio Jul 22 '15 at 20:56
  • This is where I would use an assert or exception. If `time` should always be less than 6:30 prior to entering the loop, then the loop should be preceded by `assert(time < 6:30)` or some other kind of check. – Carlton Jul 30 '15 at 15:57
  • 1
    @Carlton, time could become more than 6:30 *within* the loop, depending on what exactly happens within the `Work()` function – Antonio Jul 30 '15 at 19:11
  • @iang no, not if there is code in the body that edits `i`. It could be passed by `const&` to some function, that then static casts it back to editable and increments it an extra time (for whatever excuse; some programmer gettinf fancy and making an off by 1 error is dirt common). To prove it impossible you need to audit all uses of the loop variable for insanity. – Yakk - Adam Nevraumont Jan 03 '20 at 04:44
97

There is no technical reason. But there is mitigation of risk, maintainability and better understanding of code.

< or > are stronger restrictions than != and fulfill the exact same purpose in most cases (I'd even say in all practical cases).

There is duplicate question here; and one interesting answer.

Ely
  • 10,860
  • 4
  • 43
  • 64
  • 6
    There are some types, such as iterators, that don't support < or >, but do support == and !=. – Simon B Jul 16 '15 at 21:15
  • 1
    It's a for loop with *int*. No iterators. – Ely Jul 17 '15 at 09:36
  • 7
    *"But there is mitigation of risk, maintainability and better understanding of code."* All of which are, properly, technical reasons. :-) – T.J. Crowder Jul 18 '15 at 09:49
  • @T.J.Crowder What type of reason is it when you just want to talk about what the machines will do as a result? – Brilliand Jul 20 '15 at 21:43
  • 1
    @DanSheppard I would generally consider mitigation of risk and maintainability to be business reasons, and better understanding of code to be a social reason (although those reasons are all being applied to a technical matter in this case). "Mitigation of risk" considerations aren't in any way limited to technical matters, and "better understanding of code" considerations can change if you find yourself working with a different group of people (even if the machines involved don't change at all). – Brilliand Jul 21 '15 at 16:54
89

Yes there is a reason. If you write a (plain old index based) for loop like this

for (int i = a; i < b; ++i){}

then it works as expected for any values of a and b (ie zero iterations when a > b instead of infinite if you had used i == b;).

On the other hand, for iterators you'd write

for (auto it = begin; it != end; ++it) 

because any iterator should implement an operator!=, but not for every iterator it is possible to provide an operator<.

Also range-based for loops

for (auto e : v)

are not just fancy sugar, but they measurably reduce the chances to write wrong code.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • 4
    Nice example. I'd be interested in a case for `!=` instead of `<`. I personally never used that I think. – Ely Jul 16 '15 at 14:05
  • @Elyasin I didnt find a good one, and I didnt want to post a bad one: Imagine you have some kind of circular container, where you increment by +x modulo the size of the container N and you want to stop the loop when you reach a certain index.... well thats a really bad example. Maybe I will find a better one – 463035818_is_not_an_ai Jul 16 '15 at 14:13
  • 1
    The same loop with != instead of < makes it clear what the advantage of < (or >) is in this context. `for (int i=a; i != b; i++) {}` – Paul Smith Jul 16 '15 at 15:01
  • This is an excellent example, but I was thinking of the case where b changed in the loop. I am not sure `for(int a=1; a!=b;i++){ b-=2;}` terminates. – emory Jul 16 '15 at 19:19
  • 13
    Using anything besides `!=` with iterators is quite dangerous, because *sometimes* the iterators can be less-than compared (it's a pointer for example) but the comparison is meaningless (it's a linked list). – Zan Lynx Jul 16 '15 at 22:21
  • 2
    Learn to use white space, seriously. – Miles Rout Jul 21 '15 at 05:20
  • 1
    `for (int i=a;i – Miles Rout Jul 22 '15 at 04:44
  • Learn to write readable code, and others won't need to learn to read it. – Miles Rout Jul 23 '15 at 04:20
  • Without giving any other reason than that it is objectively poor style. – Miles Rout Jul 30 '15 at 23:36
  • @MilesRout sorry for being stubborn. maybe time to remove some spam comments. cheers ;) – 463035818_is_not_an_ai Oct 18 '18 at 17:56
72

You can have something like

for(int i = 0; i<5; ++i){
    ...
    if(...) i++;
    ...
}

If your loop variable is written by the inner code, the i!=5 might not break that loop. This is safer to check for inequality.

Edit about readability. The inequality form is way more frequently used. Therefore, this is very fast to read as there is nothing special to understand (brain load is reduced because the task is common). So it's cool for the readers to make use of these habits.

johan d
  • 2,798
  • 18
  • 26
  • 6
    This type of inner "if (condition) then i++" is the first thing I thought of. – WernerCD Jul 16 '15 at 16:41
  • 3
    Came in expecting this to be the highest voted answer; I was surprised I had to scroll this far to find it. This does a lot more toward convincing a novice programmer than "well, you should use the strongest condition". The other answers, if they stay on top, should incorporate this as a clear and obvious explanation of what can go wrong with OP's proposed code. – msouth Jul 17 '15 at 12:45
  • 1
    @msouth I fully agree, but my POV is quite subjective ;) – johan d Jul 17 '15 at 18:07
  • @johand. there is a question like this on, I think maybe a java thread ? somewhere, and one answer actually described it being a "pro" of the equality method--by getting stuck in an infinite loop, it will expose the bug of the iterator being incremented in the loop. – msouth Jul 17 '15 at 21:39
  • 3
    @msouth I love it when unexpected production behaviour exposes my bugs, don't you? :-) – deworde Jul 20 '15 at 10:14
  • 1
    @deworde Yes! Yes, that's the best place to find them, because it really gives you a sense of urgency about fixing it when you take down a large client's operations. BTW, Here's the pro/con answer I was referring to (the question is referenced above): http://stackoverflow.com/questions/1783822/format-of-for-loops/8838920#8838920 Also, the same argument is being made in comments on one of the answers above. Shaking my poor, jaded head. – msouth Jul 20 '15 at 15:41
  • 3
    @msouth Look, all we have to do is never make any mistakes ever and everything will be fine. Simples. – deworde Jul 21 '15 at 07:37
  • 1
    @deworde Ah! You mean write it *without* the bugs! I should start doing that. Seems like such an obvious idea, but sometimes it just takes that special kind of genius to point out what everyone is missing. I'm aware of the possible shock to the economy of entire QA departments suddenly becoming unemployed, so I'll roll this out slowly. – msouth Jul 21 '15 at 15:11
49

And last but not least, this is called defensive programming, meaning to always take the strongest case to avoid current and future errors influencing the program.

The only case where defensive programming is not needed is where states have been proven by pre- and post-conditions (but then, proving this is the most defensive of all programming).

Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
Paul Ogilvie
  • 25,048
  • 4
  • 23
  • 41
  • 2
    One good example in support of this: Memory is vulnerable to bit flips ([SEUs](https://en.wikipedia.org/wiki/Single_event_upset)) caused by radiation. When using an `int` for an `i != 15` condition, the counter will be running for a long time if anything above the fourth bit is flipped, especially on machines where `sizeof(int)` is 64. SEUs are a very real concern at higher altitude or in space (because of higher radiation), or in large supercomputers (because so much memory is present). – Josh Sanford Sep 20 '16 at 13:56
  • 2
    Thinking that your program will run ok when radiation is changing your memory is an optimistic way and anticatastrophic way of thinking... good comment! :) – Luis Colorado Oct 05 '16 at 05:47
38

I would argue that an expression like

for ( int i = 0 ; i < 100 ; ++i )
{
  ...
}

is more expressive of intent than is

for ( int i = 0 ; i != 100 ; ++i )
{
  ...
}

The former clearly calls out that the condition is a test for an exclusive upper bound on a range; the latter is a binary test of an exit condition. And if the body of the loop is non-trivial, it may not apparent that the index is only modified in the for statement itself.

Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135
  • 14
    "I want this loop to run at most 5 times" vs. "I want to run this loop as many times as necessary, except exactly 5 times, which I don't want." – bishop Jul 18 '15 at 23:39
  • 1
    +1 for mentioning the upper bound on a range. I think this is important for numerical ranges. != does not define a proper range in a for loop like these – element11 Jul 20 '15 at 20:25
22

Iterators are an important case when you most often use the != notation:

for(auto it = vector.begin(); it != vector.end(); ++it) {
 // do stuff
}

Granted: in practice I would write the same relying on a range-for:

for(auto & item : vector) {
 // do stuff
}

but the point remains: one normally compares iterators using == or !=.

Escualo
  • 40,844
  • 23
  • 87
  • 135
  • 2
    Iterators often only have a notion of equality / inequality and not an ordering, that's why you use `!=` for them. For instance, in a `std::vector` you _could_ use `<` as the iterator is tied to the index / pointer, but in a `std::set` there is no such strict ordering, as it walks a binary tree. – Martin C. Jul 16 '15 at 20:38
19

The loop condition is an enforced loop invariant.

Suppose you don't look at the body of the loop:

for (int i = 0; i != 5; ++i)
{
  // ?
}

in this case, you know at the start of the loop iteration that i does not equal 5.

for (int i = 0; i < 5; ++i)
{
  // ?
}

in this case, you know at the start of the loop iteration that i is less than 5.

The second is much, much more information than the first, no? Now, the programmer intent is (almost certainly) the same, but if you are looking for bugs, having confidence from reading a line of code is a good thing. And the second enforces that invariant, which means some bugs that would bite you in the first case just cannot happen (or don't cause memory corruption, say) in the second case.

You know more about the state of the program, from reading less code, with < than with !=. And on modern CPUs, they take the same amount of time as no difference.

If your i was not manipulated in the loop body, and it was always increased by 1, and it started less than 5, there would be no difference. But in order to know if it was manipulated, you'd have to confirm each of these facts.

Some of these facts are relatively easy, but you can get wrong. Checking the entire body of the loop is, however, a pain.

In C++ you can write an indexes type such that:

for( const int i : indexes(0, 5) )
{
  // ?
}

does the same thing as either of the two above for loops, even down to the compiler optimizing it down to the same code. Here, however, you know that i cannot be manipulated in the body of the loop, as it is declared const, without the code corrupting memory.

The more information you can get out of a line of code without having to understand the context, the easier it is to track down what is going wrong. < in the case of integer loops gives you more information about the state of the code at that line than != does.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
14

Yes; OpenMP doesn't parallelize loops with the != condition.

Ian Newson
  • 7,679
  • 2
  • 47
  • 80
user541686
  • 205,094
  • 128
  • 528
  • 886
14

As already said by Ian Newson, you can't reliably loop over a floating variable and exit with !=. For instance,

for (double x=0; x!=1; x+=0.1) {}

will actually loop forever, because 0.1 can't exactly be represented in floating point, hence the counter narrowly misses 1. With < it terminates.

(Note however that it's basically undefined behaviour whether you get 0.9999... as the last accepted number – which kind of violates the less-than assumption – or already exit at 1.0000000000000001.)

leftaroundabout
  • 117,950
  • 5
  • 174
  • 319
13

It may happen that the variable i is set to some large value and if you just use the != operator you will end up in an endless loop.

cbuchart
  • 10,847
  • 9
  • 53
  • 93
LSchueler
  • 1,414
  • 12
  • 23
  • 1
    Not really endless - on most implementations of C, `i` will silently wrap around when at its maximum. But yeah, most likely longer than you are prepared to wait. – Jongware Jul 19 '15 at 10:03
12

As you can see from the other numerous answers, there are reasons to use < instead of != which will help in edge cases, initial conditions, unintended loop counter modification, etc...

Honestly though, I don't think you can stress the importance of convention enough. For this example it will be easy enough for other programmers to see what you are trying to do, but it will cause a double-take. One of the jobs while programming is making it as readable and familiar to everyone as possible, so inevitably when someone has to update/change your code, it doesn't take a lot of effort to figure out what you were doing in different code blocks. If I saw someone use !=, I'd assume there was a reason they used it instead of < and if it was a large loop I'd look through the whole thing trying to figure out what you did that made that necessary... and that's wasted time.

RyanP
  • 1,898
  • 15
  • 20
10

I take the adjectival "technical" to mean language behavior/quirks and compiler side effects such as performance of generated code.

To this end, the answer is: no(*). The (*) is "please consult your processor manual". If you are working with some edge-case RISC or FPGA system, you may need to check what instructions are generated and what they cost. But if you're using pretty much any conventional modern architecture, then there is no significant processor level difference in cost between lt, eq, ne and gt.

If you are using an edge case you could find that != requires three operations (cmp, not, beq) vs two (cmp, blt xtr myo). Again, RTM in that case.

For the most part, the reasons are defensive/hardening, especially when working with pointers or complex loops. Consider

// highly contrived example
size_t count_chars(char c, const char* str, size_t len) {
    size_t count = 0;
    bool quoted = false;
    const char* p = str;
    while (p != str + len) {
        if (*p == '"') {
            quote = !quote;
            ++p;
        }
        if (*(p++) == c && !quoted)
            ++count;
    }
    return count;
}

A less contrived example would be where you are using return values to perform increments, accepting data from a user:

#include <iostream>
int main() {
    size_t len = 5, step;
    for (size_t i = 0; i != len; ) {
        std::cout << "i = " << i << ", step? " << std::flush;

        std::cin >> step;
        i += step; // here for emphasis, it could go in the for(;;)
    }
}

Try this and input the values 1, 2, 10, 999.

You could prevent this:

#include <iostream>
int main() {
    size_t len = 5, step;
    for (size_t i = 0; i != len; ) {
        std::cout << "i = " << i << ", step? " << std::flush;
        std::cin >> step;
        if (step + i > len)
            std::cout << "too much.\n";
        else
            i += step;
    }
}

But what you probably wanted was

#include <iostream>
int main() {
    size_t len = 5, step;
    for (size_t i = 0; i < len; ) {
        std::cout << "i = " << i << ", step? " << std::flush;
        std::cin >> step;
        i += step;
    }
}

There is also something of a convention bias towards <, because ordering in standard containers often relies on operator<, for instance hashing in several STL containers determines equality by saying

if (lhs < rhs) // T.operator <
    lessthan
else if (rhs < lhs) // T.operator < again
    greaterthan
else
    equal

If lhs and rhs are a user defined class writing this code as

if (lhs < rhs) // requires T.operator<
    lessthan
else if (lhs > rhs) // requires T.operator>
    greaterthan
else
    equal

The implementor has to provide two comparison functions. So < has become the favored operator.

kfsone
  • 23,617
  • 2
  • 42
  • 74
9

There are several ways to write any kind of code (usually), there just happens to be two ways in this case (three if you count <= and >=).

In this case, people prefer > and < to make sure that even if something unexpected happens in the loop (like a bug), it won't loop infinitely (BAD). Consider the following code, for example.

for (int i = 1; i != 3; i++) {
    //More Code
    i = 5; //OOPS! MISTAKE!
    //More Code
}

If we used (i < 3), we would be safe from an infinite loop because it placed a bigger restriction.

Its really your choice whether you want a mistake in your program to shut the whole thing down or keep functioning with the bug there.

Hope this helped!

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • one could argument that the infinite loop would be a good indicator of the mistake, but then another one would argue that i=5 is not necessarily a mistake – njzk2 Jul 20 '15 at 21:58
7

The most common reason to use < is convention. More programmers think of loops like this as "while the index is in range" rather than "until the index reaches the end." There's value is sticking to convention when you can.

On the other hand, many answers here are claiming that using the < form helps avoid bugs. I'd argue that in many cases this just helps hide bugs. If the loop index is supposed to reach the end value, and, instead, it actually goes beyond it, then there's something happening you didn't expect which may cause a malfunction (or be a side effect of another bug). The < will likely delay discovery of the bug. The != is more likely to lead to a stall, hang, or even a crash, which will help you spot the bug sooner. The sooner a bug is found, the cheaper it is to fix.

Note that this convention is peculiar to array and vector indexing. When traversing nearly any other type of data structure, you'd use an iterator (or pointer) and check directly for an end value. In those cases you have to be sure the iterator will reach and not overshoot the actual end value.

For example, if you're stepping through a plain C string, it's generally more common to write:

for (char *p = foo; *p != '\0'; ++p) {
  // do something with *p
}

than

int length = strlen(foo);
for (int i = 0; i < length; ++i) {
  // do something with foo[i]
}

For one thing, if the string is very long, the second form will be slower because the strlen is another pass through the string.

With a C++ std::string, you'd use a range-based for loop, a standard algorithm, or iterators, even if though the length is readily available. If you're using iterators, the convention is to use != rather than <, as in:

for (auto it = foo.begin(); it != foo.end(); ++it) { ... }

Similarly, iterating a tree or a list or a deque usually involves watching for a null pointer or other sentinel rather than checking if an index remains within a range.

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175
  • 3
    You're quite right to emphasise the importance of idiom in programming. If I look at some code and it has a familiar pattern, I don't need to waste time inferring the pattern, but can go straight to the core of it. I guess that's my main grip with Yoda comparisons - they don't look idiomatic, so I end up having to read them twice to be sure they mean what I think they mean. – Toby Speight Jul 16 '15 at 22:02
7

There are two related reasons for following this practice that both have to do with the fact that a programming language is, after all, a language that will be read by humans (among others).

(1) A bit of redundancy. In natural language we usually provide more information than is strictly necessary, much like an error correcting code. Here the extra information is that the loop variable i (see how I used redundancy here? If you didn't know what 'loop variable' means, or if you forgot the name of the variable, after reading "loop variable i" you have the full information) is less than 5 during the loop, not just different from 5. Redundancy enhances readability.

(2) Convention. Languages have specific standard ways of expressing certain situations. If you don't follow the established way of saying something, you will still be understood, but the effort for the recipient of your message is greater because certain optimisations won't work. Example:

Don't talk around the hot mash. Just illuminate the difficulty!

The first sentence is a literal translation of a German idiom. The second is a common English idiom with the main words replaced by synonyms. The result is comprehensible but takes a lot longer to understand than this:

Don't beat around the bush. Just explain the problem!

This is true even in case the synonyms used in the first version happen to fit the situation better than the conventional words in the English idiom. Similar forces are in effect when programmers read code. This is also why 5 != i and 5 > i are weird ways of putting it unless you are working in an environment in which it is standard to swap the more normal i != 5 and i < 5 in this way. Such dialect communities do exist, probably because consistency makes it easier to remember to write 5 == i instead of the natural but error prone i == 5.

  • 1
    *Ausgezeichnet*. Similarly, one would not write the test as `i < 17+(36/-3)` (even when these are constants used elsewhere; then you must write out their *names* for clarity!). – Jongware Jul 19 '15 at 10:09
7

One reason not to use this construct is floating point numbers. != is a very dangerous comparison to use with floats as it'll rarely evaluate to true even if the numbers look the same. < or > removes this risk.

Ian Newson
  • 7,679
  • 2
  • 47
  • 80
  • If your loop iterator is a floating point number, then the `!=` versus `<` is the least of your problems. – Lundin Sep 05 '17 at 08:52
6

Using relational comparisons in such cases is more of a popular habit than anything else. It gained its popularity back in the times when such conceptual considerations as iterator categories and their comparability were not considered high priority.

I'd say that one should prefer to use equality comparisons instead of relational comparisons whenever possible, since equality comparisons impose less requirements on the values being compared. Being EqualityComparable is a lesser requirement than being LessThanComparable.

Another example that demonstrates the wider applicability of equality comparison in such contexts is the popular conundrum with implementing unsigned iteration down to 0. It can be done as

for (unsigned i = 42; i != -1; --i)
  ...

Note that the above is equally applicable to both signed and unsigned iteration, while the relational version breaks down with unsigned types.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
2

Besides the examples, where the loop variable will (unintentional) change inside the body, there are other reasions to use the smaller-than or greater-than operators:

  • Negations make code harder to understand
  • < or > is only one char, but != two
MiCo
  • 399
  • 2
  • 6
  • 4
    The second bullet is at best a frivolous reason. The code should be correct and clear. Compact is far less important. – Jonathan Leffler Jul 17 '15 at 12:27
  • @JonathanLeffler Well, in a TDD REPL, that extra character is one extra nano-second for parsing, and times a billion iterations to find the bug caused by writing `5 != $i`, that just took one whole second of my life. Uh... *snicker* – bishop Jul 18 '15 at 23:42
1

In addition to the various people who have mentioned that it mitigates risk, it also reduces the number of function overloads necessary to interact with various standard library components. As an example, if you want your type to be storable in a std::set, or used as a key for std::map, or used with some of the searching and sorting algorithms, the standard library usually uses std::less to compare objects as most algorithms only need a strict weak ordering. Thus it becomes a good habit to use the < comparisons instead of != comparisons (where it makes sense, of course).

Andre Kostur
  • 770
  • 1
  • 6
  • 15
  • 1
    you may be right on the number of overloads, but when considering the requirements on the objects, then for almost any object one can define a `!=` operator, but it is not always possible to define a strict weak ordering. In this sense `!=` would be better, because it puts less requirements on the type. However, I still stay with the `<`. – 463035818_is_not_an_ai Jul 16 '15 at 14:17
0

There is no problem from a syntax perspective, but the logic behind that expression 5!=i is not sound.

In my opinion, using != to set the bounds of a for loop is not logically sound because a for loop either increments or decrements the iteration index, so setting the loop to iterate until the iteration index becomes out of bounds (!= to something) is not a proper implementation.

It will work, but it is prone to misbehavior since the boundary data handling is lost when using != for an incremental problem (meaning that you know from the start if it increments or decrements), that's why instead of != the <>>==> are used.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • 2
    The logic is perfectly fine. When `i` is `5` the loop exits. Did you mean to say something else? – Dan Getz Jul 16 '15 at 18:56
  • 1
    If the data does not get transferred to correctly due to heat, electro.magnetic influences your code runs forever. If you do this on a regular workstation or server with ECC RAM there is not issue (neither logical, technical nor physical) – Markus Jul 16 '15 at 19:21