views:

745

answers:

8
/**
  * Returns a number between kLowerBound and kUpperBound
  * e.g.: Wrap(-1, 0, 4); // Returns 4
  * e.g.: Wrap(5, 0, 4); // Returns 0      
  */
int Wrap(int const kX, int const kLowerBound, int const kUpperBound)
{
    // Suggest an implementation?
}
A: 

For negative kX, you can add:

int temp = kUpperBound - kLowerBound + 1;
while (kX < 0) kX += temp;
return kX%temp + kLowerBound;
klew
Works but not elegant. Try "{temp2 = -kX/temp; kX += temp2+1;}"
dmckee
should be "if (kX < 0) {...};"
dmckee
This doesn't work with a non-zero lower bound. E.g. lb = 3, ub = 8, kX = 5. Clearly 5 is already in range, but temp = 6 and 5 % 6 + 3 = 8 != 5.
Charles Bailey
A: 

Actually, since -1 % 4 returns -1 on every system I've even been on, the simple mod solution doesn't work. I would try:

int range = kUpperBound  - kLowerBound +1;
kx = ((kx - kLowerBound) % range) + range;
return (kx % range) + kLowerBound;

if kx is positive, you mod, add range, and mod back, undoing the add. If kx is negative, you mod, add range which makes it positive, then mod again, which doesn't do anything.

Brian Postow
It's better than my 'while' :)
klew
Best so far, I think.
dmckee
Try lb = 3, ub = 8, kx = 5. Clearly kx is in range so should gives 5 but... line 2: kx = (5 % 6) + 6 == 11, then in line 3: (kx % 6) + 3 = 5 + 3 == 8 != 5.
Charles Bailey
Just because it does that on every system you know, doesn't mean it's right.
rlbond
must read kx=((kx-kLowerBound)%range)+range in the second line.
MartinStettner
+8  A: 

The sign of a % b is only defined if a and b are both non-negative.

int Wrap(int kX, int const kLowerBound, int const kUpperBound)
{
    int range_size = kUpperBound - kLowerBound + 1;

    if (kX < kLowerBound)
        kX += range_size * ((kLowerBound - kX) / range_size + 1);

    return kLowerBound + (kX - kLowerBound) % range_size;
}
Charles Bailey
The first solution that works for negative numbers, without relying on unspecified behavior or looping.
Mark Ransom
I hope so... given how deceptively 'simple' the question appears, I wouldn't be surprised to have made mistake. It does rely on kUpperBound >= kLowerBound.
Charles Bailey
For Wrap(-1, 1, 4)) it gives 3. I think it should return 4.
klew
For a range of 1 -> 4, surely 0 == 4 and -1 == 3?
Charles Bailey
Ok, you're right. My answer treat kX as some kind of index of low and high bound.
klew
I like this solution best so far. That said, you could technically do a return from inside the if, couldn't you? You're bringing kX into range at that point, so return kX +, versus kX+= followed by another modulus, no?
Eddie Parker
Yes, true. And you can do the second half in a similar way, without a modulus too.
Charles Bailey
minor point: should you change the function signature to use "int kX" not "int const kX" since you're changing kX internally?
Mr Fooz
+5  A: 

The following should work independently of the implementation of the mod operator:

int range = kUpperBound - kLowerBound + 1;
kx = ((kx-kLowerBound) % range);
if (kx<0)
  return kUpperBound + 1 + kx;
else
  return kLowerBound + kx;

An advantage over other solutions is, that it uses only a single % (i.e. division), which makes it pretty efficient.

Note (Off Topic):

It's a good example, why sometimes it is wise to define intervals with the upper bound being being the first element not in the range (such as for STL iterators...). In this case, both "+1" would vanish.

MartinStettner
Very nice, and excellent point on the +1s.
Brian Postow
A: 

I would suggest this solution:

int Wrap(int const kX, int const kLowerBound, int const kUpperBound)
{
    int d = kUpperBound - kLowerBound + 1;
    return kLowerBound + (kX >= 0 ? kX % d : -kX % d ? d - (-kX % d) : 0);
}

The if-then-else logic of the ?: operator makes sure that both operands of % are nonnegative.

A: 

An answer that has some symmetry and also makes it obvious that when kX is in range, it is returned unmodified.

int Wrap(int const kX, int const kLowerBound, int const kUpperBound)
{
    int range_size = kUpperBound - kLowerBound + 1;

    if (kX < kLowerBound)
        return kX + range_size * ((kLowerBound - kX) / range_size + 1);

    if (kX > kUpperBound)
        return kX - range_size * ((kX - kUpperBound) / range_size + 1);

    return kX;
}
Charles Bailey
A: 

I would give an entry point to the most common case lowerBound=0, upperBound=N-1. And call this function in the general case. No mod computation is done where I is already in range. It assumes upper>=lower, or n>0.

int wrapN(int i,int n)
{
  if (i<0) return (n-1)-(-1-i)%n; // -1-i is >=0
  if (i>=n) return i%n;
  return i; // In range, no mod
}

int wrapLU(int i,int lower,int upper)
{
  return lower+wrapN(i-lower,1+upper-lower);
}
Eric Bainville
A: 

Fastest solution, least flexible: Take advantage of native datatypes that will do wrapping in the hardware.

The absolute fastest method for wrapping integers would be to make sure your data is scaled to int8/int16/int32 or whatever native datatype. Then when you need your data to wrap the native data type will be done in hardware! Very painless and orders of magnitude faster than any software wrapping implementation seen here.

As an example case study:

I have found this to be very useful when I need a fast implementation of sin/cos implemented using a look-up-table for a sin/cos implementation. Basically you make scale your data such that INT16_MAX is pi and INT16_MIN is -pi. Then have you are set to go.

As a side note, scaling your data will add some up front finite computation cost that usually looks something like:

int fixedPoint = (int)( floatingPoint * SCALING_FACTOR + 0.5 )

Feel free to exchange int for something else you want like int8_t / int16_t / int32_t.


Next fastest solution, more flexible: The mod operation is slow instead if possible try to use bit masks!

Most of the solutions I skimmed are functionally correct... but they are dependent on the mod operation.

The mod operation is very slow because it is essentially doing a hardware division. The laymans explanation of why mod and division are slow is to equate the division operation to some pseudo-code for(quotient = 0;inputNum> 0;inputNum -= divisor) { quotient++; } ( def of quotient and divisor ). As you can see, the hardware division can be fast if it is a low number relative to the divisor... but division can also be horribly slow if it is much greater than the divisor.

If you can scale your data to a power of two then you can use a bit mask which will execute in one cycle ( on 99% of all platforms ) and your speed improvement will be approximately one order of magnitude ( at the very least 2 or 3 times faster ).

C code to implement wrapping:

#define BIT_MASK (0xFFFF)
int wrappedAddition(int a, int b) {
    return ( a + b ) & BIT_MASK;
}
int wrappedSubtraction(int a, int b) {
    return ( a - b ) & BIT_MASK;
}

Feel free to make the #define something that is run time. And feel free to adjust the bit mask to be whatever power of two that you need. Like 0xFFFFFFFF or power of two you decide on implementing.


p.s. I strongly suggest reading about fixed point processing when messing with wrapping/overflow conditions. I suggest reading:

Fixed-Point Arithmetic: An Introduction by Randy Yates August 23, 2007

Trevor Boyd Smith