views:

266

answers:

12

I want to decrease a value by one and if it reaches zero, set it to the maximum value. Is there a way to do this via math without resorting to if (n-1 == 0) { n = max; }

The opposite scenario of increasing a value by one and then setting it to zero when it is greater than max can easily be achieved using n = (n + 1) % (max + 1);. Furthermore, this is even better since you can increase by whatever amount (not just one) and it will still "wrap" correctly.

Thanks for the answers so far. To be clear, I meant without any boolean logic (if/else) or boolean operators (!, &&, etc) at all. I was just curious as to how to do this. Does the correct answer below really make it more unreadable as long as a comment is provided? It would be necessary to use that for the more general case for subtracting an arbitrary number and expecting the correct wrap around.

+6  A: 
n = max - ((max - n +1)%max)
Ofir
That answers the question and raises a new one: why would one replace a simple statement with a complicated one that does the same thing?
Ozan
+1  A: 

The problem is that the % operator in C returns a negative result when faced with a negative dividend. This is often not what is needed.

If you want a mod function that will fulfill -1 mod n == n-1, then, in C, you have to do the following:

int mod(int a, int b)
{
    return (a%b + b) % b;
}

You can then do

n=mod(n-1, max+1);

to decrement n and have it wrap around to max when n==0. Note that, as for the increment case, this will work for arbitrary step sizes.

Martin B
It's actually worse than that: '%' operator in C can return either a negative *or* positive result when the dividend is negative. All that's required is that you can put the results from `/` and `%` back together into the original number.
Jerry Coffin
@Jerry: That was true under C89. However, the current C standard requires that integer division truncate towards zero. Together with the requirement you mention, this fully specifies the `%` operator.
Stephen Canon
+1  A: 

There are enough variations in math between languages that I doubt there's a language-agnostic way to do this. There's simply too much variation in how languages write expressions for a basic single technique to work with every possible language.

If you pick a specific language, there's a pretty good chance that it's possible. For example, in C or C++, you could do something like: n = (n-1) + ((n-1) == 0) * max;

In case you care how that works: in C, a comparison ( == in this case) produces a result of 0 for false, and 1 for true. So what we're doing is adding max * 0 when/if n-1 != 0, and max * 1 when/if n-1 == 0.

Jerry Coffin
A: 

Why not just compare to 1?

if(n==1) { n = max; }

Joel
What I asked in the question was an alternative to comparing it as you are.
GreenieMeanie
+4  A: 

If you're doing this purely for performance reasons then I would advise against it. % is usually quite an expensive operation on most architectures - a simple comparison, branch and addition will usually be more efficient. Of course the usual caveats about speculative/premature optimisation apply.

Paul R
And the usual caveat that architectures vary quite a bit, and change fairly fast and not in predictable ways. I've given up trying to second-guess anything except cache locality.
David Thornley
I tried compiling (with full optimization) and disassembling, and it looks like you're right: not only is the original way shorter, it has fewer branches (!). I guess in hardware you need to do a division to get the modulo, and division isn't cheap or easy.
Ken
A: 

There might be a better way but I think n = max - (max - n + 1) % (max + 1) works. I'm assuming you want to include 0 at both ends since for your increment expression you do include 0.

tobyclemson
A: 

In any short-circuit logic evaluation language (most modern languages) you can do something like this:

--n<=0 && n=max;

You might get the hairy eyeball from some of your coworkers if your team isn't accustomed to using && as an if-then operator.

To do the forward looping increment:

++n>max && n=1;

These examples are assuming a 1-indexed counter since your question seems to suppose that. 0-indexed equivalents are:

--n<0 && n=max-1;

++n>=max && n=0;

MightyE
The --n wouldn't work for an unsigned type which is presumably the reason he's avoiding the compare.
Joel
MightyE
A: 

Actually you can do also

n = (n - 1) + ((!(n - 1)) * max);
antti.huima
A: 

n = n>1 ? n-1 : max;

Ray
The ternary operator is using a boolean logic and hence the wrong answer.
Ritsaert Hornstra
Thanks. Look around, there are more boolean operators. :]
Ray
The requirement for no boolean operator was added after this and other solutions (such as mine) were added (see http://stackoverflow.com/posts/2282297/revisions)
MightyE
A: 

How about this:
n = !n*max + (!!n)*n;

frankc
A: 

Re: no booleans
Well, through the magic of integer division (C-style), my previous answer can be written as:
n = ((max-n)/max) * max + ((max+n-1)/max)*n;

frankc
A: 

Just to ask: why do you want to avoid a boolean operation?

If you want to avoid conditional code in your application you might use boolean expressions that are store in boolean values. Those will be mapped to the SETcc instructions on an i386 and I assume analog instuctions exist on other ISAs.

In that case you can use a boolean expression and still have non conditional code and you could use:

Under the assumption that a boolean result of true equals to the Ordinal 1 (this is the case in Delphi code) and a boolean value of false equals to 0 you could write

next := current - 1 + ( ( 1 + max ) and -Ord( current = 0 ) );

Don't vote this down because I gave an answer with boolean operations. I just want to check if this is a differnet solution to the underlying problem.

Still: I think the conditional code is much much more readable.

Ritsaert Hornstra