Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I have explained to my students that equal-to testing is not reliable for float variables, but is fine for integers. The textbook I am using said that it is easier to read > and < than >= and <=. I agree to some extent, but in a For loop? Isn't it clearer to have the loop specify the starting and ending values?

Am I missing something that the textbook author is correct about?

Another example is in Range tests like:

if score > 89 grade = 'A'
else if score > 79 grade = 'B' ...

Why not just say: if score >= 90 ?

share|improve this question

closed as primarily opinion-based by gnat, Ixrec, Daenyth, zzzzBov, Bart van Ingen Schenau Mar 19 at 12:27

Many good questions generate some degree of opinion based on expert experience, but answers to this question will tend to be almost entirely based on opinions, rather than facts, references, or specific expertise.If this question can be reworded to fit the rules in the help center, please edit the question.

11  
Unfortunately, since there is no objective difference in behavior between these options, this amounts to an opinion poll on what people consider more intuitive, and polls are not suitable for StackExchange sites like this one. – Ixrec Mar 18 at 15:10
1  
It doesn't matter. Really. – Auberon Mar 18 at 15:10
3  
Actually, this is objectively answerable. Stand by... – Robert Harvey Mar 18 at 15:11
9  
@Ixrec I always find it interesting that "best practice" is not considered a suitable topic. Who doesn't want to improve, or make their code more readable? If people disagree, we all learn more sides of the issue, and might... even... change-our-minds! Ugh, that was so hard to say. Robert Harvey says he can answer it objectively. This will be interesting. – no comprende Mar 18 at 15:16
1  
@nocomprende Mostly because "best practice" is an extremely vague and overused term which can refer useful advice based on objective facts about the language, but just as often refers to the "most popular opinion" (or the opinion of whoever's using the term) when in reality all options are equally valid and invalid. In this case, you can only make an objective argument by restricting the answer to certain types of loops as Robert did, and as you pointed out yourself in a comment that does not completely answer the question. – Ixrec Mar 18 at 15:26

In curly-braced programming languages with zero-based arrays, it's customary to write for loops like this:

for (int i = 0; i < array.Length, i++) { }

This traverses all of the elements in the array, and is by far the most common case. It avoids the use of <= or >=.

The only time this would ever have to change is when you need to skip the first or last element, or traverse it in the opposite direction, or traverse it from a different start point or to a different end point.

For collections, in languages that support iterators, it is more common to see this:

foreach (var item in list) { }

Which avoids the comparisons entirely.

If you're looking for a hard and fast rule as to when to use <= vs <, there isn't one; use what best expresses your intent. If your code needs to express the concept "Less than or equal to 55 miles per hour," then it needs to say <=, not <.

To answer your question about the grade ranges, >= 90 makes more sense, because 90 is the actual boundary value, not 89.

share|improve this answer
9  
It doesn't avoid the comparisons entirely, it just sweeps them under the rug by moving them into the enumerator implementation. :P – Mason Wheeler Mar 18 at 15:16
2  
Of course, but that's no longer traversing an array, is it? – Robert Harvey Mar 18 at 15:24
4  
Because arrays are the most common use case for for loops like this. The form of for loop I provided here will be instantly recognizable to any developer with a modicum of experience. If you want a more specific answer based on a more specific scenario, you need to include that in your question. – Robert Harvey Mar 18 at 15:41
3  
"use what best expresses your intent" <- This! – Jasper N. Brouwer Mar 18 at 19:58
3  
@JasperN.Brouwer It's like a zeroth law of programming. All style rules and coding conventions collapse in deepest shame when their mandates conflict with the code's clarity. – Iwillnotexist Idonotexist Mar 19 at 6:17

It doesn't matter.

But for the sake of the argument, let's analyse the two options: a > b vs a >= b.

Hang on! Those are not equivalent!
OK, then a >= b -1 vs a > b or a > b vs a >= b +1.

Hm, a >b and a >= b both look better than a >= b - 1 and a >= b +1. What are all these 1s anyway? So I'd argue that any benefit from having > instead of >= or vice-versa is eliminated by having to add or subtract random 1s.

But what if it's a number? Is it better to say a > 7 or a >= 6? Wait a second. Are we seriously arguing whether it's better to use > vs >= and ignore the hard coded variables? So it really becomes a question of whether a > DAYS_OF_WEEK is better than a >= DAYS_OF_WEEK_MINUS_ONE... or is it a > NUMBER_OF_LEGS_IN_INSECT_PLUS_ONE vs a >= NUMBER_OF_LEGS_IN_INSECT? And we are back to adding/subtracting 1s, only this time in variable names. Or maybe debating if it's best to use threshold, limit, maximum.

And it looks like there's no general rule: it depends on what's being compared

But really, there are far more important things to improve in one's code and far more objective and reasonable guidelines (e.g. X-character limit per line) that still have exceptions.

share|improve this answer
1  
OK, there is no general rule. (Wondering why the textbook seemed to state a general rule, but I can let it go.) There is no emerging consensus that it was a memo I missed. – no comprende Mar 18 at 15:58
2  
@nocomprende because coding and formatting standards are both purely subjective and highly contentious. Most of the time none of them matter, but programmers still wage holy wars over them. (they only matter when sloppy code is likely to result in bugs) – Snowman Mar 18 at 16:21
1  
I've seen a lot of crazy discussions about coding standards but this one might just take the cake. Really silly. – JimmyJames Mar 18 at 16:31
    
@JimmyJames is this about the discussion of > vs >= or the discussion about whether the discussion of > vs >= is meaningful? although it's probably best to avoid discussing this :p – Thanos Tintinidis Mar 18 at 17:59
2  
“Programs are meant to be read by humans and only incidentally for computers to execute” - Donald Knuth. Use the style which makes it easiest to read, understand, and maintain. – user9999999 Mar 18 at 18:43

Computationally there is no difference in cost when using < or > compared to <= or >=. It's computed equally as fast.

However most for loops will be counting from 0 (because many languages use 0 indexing for their arrays). So the canonical for loop in those languages is

for(int i = 0; i < length; i++){
   array[i] = //...
   //...
}

doing this with a <= would require you to add a -1 somewhere to avoid the off-by one error

for(int i = 1; i <= length; i++){
   array[i-1] = //...
   //...
}

or

for(int i = 0; i <= length-1; i++){
   array[i] = //...
   //...
}

Of course if the language uses 1-based indexing then you would use <= as the limiting condition.

Key is that the values expressed in the condition are the ones from the problem description. It's cleaner to read

if(x >= 10 && x < 20){

} else if(x >= 20 && x < 30){

}

for a half-open interval than

if(x >= 10 && x <= 19){

} else if(x >= 20 && x <= 29){

}

and have to do the math to know that there is no possible value between 19 and 20

share|improve this answer
    
I see the "length" idea, but what if you are just using values that came from somewhere and are meant to iterate from a starting value to an ending value. A class example was Markup percentage from 5 to 10. Isn't it clearer to say: for ( markup = 5 ; markup <= 10 ; markup++ ) ... ? Why would I change to it test: markup < 11 ? – no comprende Mar 18 at 15:20
6  
@nocomprende you wouldn't, if the boundary values from your problem description are 5 and 10 then it's better to have them explicitly in the code rather than a slightly changed value. – ratchet freak Mar 18 at 15:47
    
@nocomprende The right format is more obvious when the upper bound is a parameter: for(markup = 5; markup <= MAX_MARKUP; ++markup). Anything else would be overcomplicating it. – Navin Mar 19 at 4:48

As you pointed out in your question, testing for equality on float variables is not reliable.

The same holds true for <= and >=.

However, there is no such reliability issue for integer types. In my opinion, the author was expressing her opinion as to which is more readable.

Whether or not you agree with him is of course your opinion.

share|improve this answer
    
Is this a generally held view, by other authors and grey-hairs in the field (I have grey hair, actually)? If it is just "One Reporter's Opinion" I can tell the students that. I have, actually. Never heard of this idea before. – no comprende Mar 18 at 15:38
    
Author's gender is irrelevant, but I updated my answer anyway. I prefer to select < or <= based on what's most natural for the particular problem I'm solving. As others have pointed out, in a FOR loop < makes more sense in C-type languages. There are other use cases that favor <=. Use all the tools at your disposal, when and where appropriate. – Dan Pichelman Mar 18 at 15:44
    
Disagree. There can be reliability issues with integer types: in C, consider: for (unsigned int i = n; i >= 0; i--) or for (unsigned int i = x; i <= y; i++) if y happens to be UINT_MAX. Oops, those loop forever. – jamesdlin Mar 19 at 6:30

I'd say that the point is not whether you should use > or >=. The point is to use whatever let's you write expressive code.

If you find that you need to add/subtract one, consider using the other operator. I find that good things happen when you start out with a good model of your domain. Then the logic writes itself.

bool IsSpeeding(int kilometersPerHour)
{
    const int speedLimit = 90;
    return kilometersPerHour > speedLimit;
}

This is way more expressive than

bool IsSpeeding(int kilometersPerHour)
{
    const int speedLimit = 90;
    return kilometersPerHour >= (speedLimit + 1);
}

In other cases, the other way is preferable:

bool CanAfford(decimal price, decimal balance)
{
    return balance >= price;
}

Much better than

bool CanAfford(decimal price, decimal balance)
{
    const decimal epsilon = 0e-10m;
    return balance > (price - epsilon);
}

Please excuse the "primitive obsession", obviously you'd want to use a Velocity- and Money-type here respectively, but I omitted them for brevity. The point is: Use the version that is more concise and that lets you focus on the business problem you want to solve.

share|improve this answer
2  
Speed limit example is a poor example. Going 90kph in a 90kph zone is not considered speeding. A speed limit is inclusive. – eidsonator Mar 18 at 19:29
    
you are absolutely correct, brain fart on my part. feel free to edit it to use a better example, hopefully the point isn't completely lost. – kai Mar 18 at 19:37

Each of the relations <, <=, >=, > and also == and != have their use-cases for comparing two floating-point values. Each has a specific meaning and the appropriate one should be chosen.

I'll give examples for cases where you want exactly this operator for each of them. (Be aware of NaNs, though.)

  • You have an expensive pure function f that takes a floating-point value as input. In order to speed up your computations, you decide to add a cache of the most recently computed values, that is, a lookup table mapping x to f(x). You'll really want to use == to compare the arguments.
  • You want to know whether you can meaningfully divide by some number x? You probably want to use x != 0.0.
  • You want to know whether a value x is in the unit interval? (x >= 0.0) && (x < 1.0) is the correct condition.
  • You have computed the determinant d of a matrix and want to tell whether it is positive definite? There is no reason to use anything else but d > 0.0.
  • You want to know whether Σn = 1, …, ∞ n−α diverges? I'd test for alpha <= 1.0.

Floating-point math (in general) is not exact. But that doesn't mean that you should fear it, treat it as magic, and certainly not always treat two floating-point quantities equal if they are within 1.0E-10. Doing so will really break your math and cause all weird things to happen.

  • For example, if you use fuzzy comparison with the cache mentioned above, you'll introduce hilarious errors. But even worse, the function will no longer be pure and the error depend on the previously computed results!
  • Yes, even if x != 0.0 and y is a finite floating-point value, y / x need not be finite. But it might be relevant to know whether y / x is not finite because of overflow or because the operation was not mathematically well-defined to begin with.
  • If you have a function that has as a precondition that an input parameter x has to be in the unit interval [0, 1), I would be really upset if it fired an assertion failure when called with x == 0.0 or x == 1.0 - 1.0E-14.
  • The determinant you have computed might not be accurate. But if you're going to pretend that the matrix is not positive definite when the computed determinant is 1.0E-30, nothing is gained. All you did was increasing the likelihood of giving the wrong answer.
  • Yes, your argument alpha might be affected by rounding errors and therefore alpha <= 1.0 might be true even though the true mathematical value for the expression alpha was computed from might have been truly greater than 1. But there is nothing you could possibly do about it at this point.

As always in software engineering, handle errors at the appropriate level and handle them only once. If you add rounding errors on the order of 1.0E-10 (this seems to be the magic value most people use, I don't know why) each time you're comparing floating-point quantities, you'll soon be at errors on the order of 1.0E+10

share|improve this answer
    
A most excellent answer, though admittedly, only for a different question than the one asked. – Dewi Morgan Apr 20 at 0:36

The type of conditional used in a loop may limit the kinds of optimizations a compiler can perform, for better or for worse. For example, given:

uint16_t n = ...;
for (uint16_t i=1; i<=n; i++)
  ...  [loop doesn't modify i]

a compiler could assume that the above condition should cause the loop to exit after the nth pass loop unless n might 65535 and the loop might exit in some fashion other than by i exceeding n. If those conditions apply, the compiler must generate code which would cause the loop to run until something other than the above condition causes it to exit.

If the loop had instead been written as:

uint16_t n = ...;
for (uint16_t ctr=0; ctr<n; ctr++)
{
  uint16_t i = ctr+1;
  ... [loop doesn't modify ctr]
}

then a compiler could safely assume that the loop would never need to execute more than n times and may thus be able to generate more efficient code.

Note that any overflow with signed types can have nasty consequences. Given:

int total=0;
int start,lim,mult; // Initialize values somehow...
for (int i=start; i<=lim; i++)
  total+=i*mult;

A compiler might rewrite that as:

int total=0;
int start,lim,mult; // Initialize values somehow...
int loop_top = lim*mult;
for (int i=start; i<=loop_top; i+=mult)
  total+=i;

Such a loop would behave identically to the original if no overflow occur in the calculations, but could run forever even on hardware platforms where integer overflow would normally have consistent wrapping semantics.

share|improve this answer
    
Yeah, I think that is a little farther along in the textbook. Not a consideration yet. Compiler Optimizations are useful to understand, and overflow has to be avoided, but it was not really the motivation for my question, which was more in terms of how people understand the code. Is it easier to read x > 5 or x >= 6? Well, it depends... – no comprende Mar 18 at 19:43
    
Unless you're writing for an Arduino the max value for int is going to be a bit higher than 65535. – Corey Mar 19 at 8:59
1  
@Corey: The max value for uint16_t is going to be 65535 on any platform where the type exists; I used uint16_t in the first example because I would expect more people to know the exact maximum value of a uint16_t than that of a uint32_t. The same point would apply in either case. The second example is agnostic with regard to the type of "int", and represents a critical behavioral point many people fail to recognize about modern compilers. – supercat Mar 19 at 13:58

Not the answer you're looking for? Browse other questions tagged or ask your own question.