tags:

views:

1935

answers:

7

I have a class which represents a shape. The Shape class has a property called Angle. I want the setter for this property to automatically wrap the value into the range [0,359].

Unfortunately, a simple _Angle = value % 360; only works for positive numbers. In C#, -40 % 360 == -40. Google calc does it the way I want it. The value should be 320.

What's the most elegant solution in C#?

Here's the best way I've got so far:

     public double Angle {
        get { return _Angle; } 
        set {
            if ( value >= 0 ) {
                _Angle = value % 360;
            }
            else {
                _Angle = value - (360 * ((int)(value / 360) - 1)); 
            }
        }
    }

Edit:

Thanks guys, I now have:

     public double Angle {
        get { return _Angle; } 
        set {
            _Angle = (value % 360) + ((value < 0) ? 360 : 0);
        }
    }

..Which is a lot better :)

+1  A: 
ja
I thought of that, but what if a value less than -360 is passed?
Blorgbeard
Yes, I am assuming here that the Angle is normalized at every update.
ja
A: 
(360 * Math.floor(Math.abs(value) / 360) + value) % 360
vava
A: 

If your values aren't going to be wildly out of the range you can do a little loop.

while (value < 0) {
  value = value + 360;
}
while (value > 360) {
  value = value - 360;
}
John Meagher
+7  A: 

Although this is for Java, Java also has the same behavior for modulus. (i.e. -40 % 360 == -40).

The below code should return an answer from [0. 360), regardless of the given angle, positive or negative.

public class Mod
{
    public static int mod(int a, int b)
    {
     if (a < 0)
      return b + (a % b);
     else
      return a % b;
    }

    public static void main(String[] args)
    {
     System.out.println(mod(40, 360)); // 40
     System.out.println(mod(-40, 360)); // 320
     System.out.println(mod(-400, 360)); // 320
    }
}

Note that works when the given angle is past -360.

coobird
wow this is way too complex, you really don't need that extra if statement.
Nick Berardi
Hah, of course! Much simpler than my code.
Blorgbeard
+3  A: 

This should give you the required results

public double Angle {
    get { return _Angle; }
    set { _Angle = value % 360 + (value % 360 < 0 : 360 : 0); }
}

I am assuming that 360 is degrees and you are trying to find where in the {0, 360} the angle lies.

Nick Berardi
(-1) you berate coobird for an unneeded if, and your code (incorrectly typed) uses ?:. Do you think the compiler does not generate a conditional for this?
Robert Lamb
Well (-1) back at you, it is not how the he used the IF statement, it was the fact he didn't satisfy what the poster asked for. he already had an IF statement, the author wanted to minimize his code, adding another method with an IF statement in it didn't do that
Nick Berardi
+3  A: 

While your solution works for the problem you have the algorithm is actually not identical to the one used by Google. It differs if you use a negative divisor.

public double GoogleModulo(double value, double divisor)
{
    long q = (long)Math.Floor(value / divisor);
    return value - q * divisor;
}

Console.WriteLine(GoogleModulo(  40,  360)); //   40
Console.WriteLine(GoogleModulo( -40,  360)); //  320
Console.WriteLine(GoogleModulo(-400,  360)); //  320
Console.WriteLine(GoogleModulo(  40, -360)); // -320

Check google's response to the last calculation here.

The algorithm is explained on wikipedia and attributed to Donald Knuth.

jesperll
+2  A: 

The mod operation is very slow. If possible replace with bit mask.

coobird's code is pretty good... but is very slow because it is doing a mod operation. If it is possible to scale your data to be within some power of two range, then you can improve the speed by approximately an order of magnitude ( at the very least 2 or 3 times faster ) by using a bit mask.

C code:

#define BIT_MASK (0xFFFF)
if (a < 0) {
    return b + (a & BIT_MASK);
} else {
    return a & 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.

Trevor Boyd Smith
Unless the setting of the angle is taking a large amount of the CPU time I think isn't worth the effort. My current computer can do 450 millons modulus operations for small angles using 2 threads. Using Bit operations was 2.5-3 times faster. In 1 millon operations it saves only 1.5 millisecond.
ggf31416
If 2 to 3 x performance increase is not 'good', your problem sounds like you are doing premature optimization on something small ( i.e. wrapping numbers ) when you should be focusing on bigger problems.A lot of the stuff I work on has a hard real time deadline and must exec in micro secs of time.
Trevor Boyd Smith
nice optimisation Trevor
Anonymous Type