tags:

views:

408

answers:

9

I have an enumeration MyEnum (Neg -1; None 0; Pos 1).

I use the result of CompareTo() to initialize a object of that enum.

What is the most performant way in .NET to do it (Negative to -1, 0 to 0, Positive to 1)?

NB.
CompareTo() returns an Integer value..

A: 

If I understand that correctly, you want to return -1 for negative values, 0 for zero values and 1 for positive values.

I'd go with:

public static MyEnum GetSign(int value)
{
    return value == 0 ? 0 : (MyEnum)(value / Math.Abs(value));
}

Or did I get something wrong?

Turing Complete
first of all, CompareTo return integer values, secondly, I need the most performant way.
serhio
we are in the 21st century. afaik integer divisions are no slower than any other operations on modern cpus.
Turing Complete
My guess is you would call this with `GetSign(a.compareTo(b));`
glowcoder
There is an input value for which this will throw.
AakashM
@Turing Complete: How do you imagine `Division` and `Comparation` be performed at the same number of ticks, "on modern CPUs"?
serhio
see test results bellow
serhio
+11  A: 

there's a builtin method called Math.Sign in the .net framework

knittl
+13  A: 

Have a look at Math.Sign

Henrik
this method shows best results, according to the test results bellow.
serhio
+4  A: 
Math.Sign(value)
mbeckish
+10  A: 

As @Henrik and @knittl have said you should use Math.Sign. But if you're interested as to what the .Net framework is doing behind the scenes the following code has come out of Reflector.

public static int Sign(int value)
{
  if (value < 0)
  {
    return -1;
  }
  if (value > 0)
  {
    return 1;
  }
  return 0;
}
Stevo3000
I am interested in the most performant method. Does the custom function behave quicker that the built-in one?
serhio
premature optimization is the root of all evil
knittl
serhio, the function above **IS** the built-in one.
Lirik
see test results bellow
serhio
A: 

You could check the most significant bit of the variable (in two's complement, if the most significant bit is 1, you have a negative number). If your most significant bit is 0, check if the value is 0. Return a value accordingly.

int Sign(int value){
    if (value >> 31) { //Shifts the variable over 31 places, if the MSB is 1, the statement is true
         return -1;
    } else {
        if (value == 0){
            return 0;
        } else {
            return 1;
        }
    }  }  

(edited for example)

Jan Gorzny
a code example?
serhio
@serhio: This is your homework, not ours.
GvS
@GvS: thinking like this I should search the answer in books, and not asking in forum. Now if I don't know how to transform a negative to -1, do you think I am able to check the most significant bit in a integer? :)
serhio
Your code doesn't compile.
Joren
@Joren thanks, I changed it; it should now compile as c++ code (that was my intention, but it came out looking like Java).
Jan Gorzny
Jan, you don't need "else", cause you do return... now after the number of operation does not Stevo3000 solution be more optimal?..
serhio
True, the 'else' statements are unnecessary. As for which is more optimal, I'm not entirely sure--I'm just offering an alternative. You can test which is quicker or allow someone else to comment on the performance difference. I think it comes down to whether '<' is faster/slower than '<<'.
Jan Gorzny
@Jan: I was thinking more of `if (value >> 31 == 1)` to make it valid C# (a more likely .NET language) but sure, whatever. :)
Joren
see test results above
serhio
+3  A: 

Result of CompareTo is negative, zero, or positive. If you look at the other answers, Math.Sign uses 2 if statements to return an int.

Just re-code Math.Sign to return your enum.

(If this was not homework I would give a code example there, but you are supposed to learn from it).

After that, test it, to see what is the most performant.

GvS
Stevo3000 gave an example.
serhio
However, a code example would be appreciated.
serhio
In the time you spend waiting for an answer, you could have created 10 code examples yourself, done 20 things wrong, and so learned 20 things that are not in any code example ;-)
GvS
see test results bellow :) thanks.
serhio
A custom sign function is faster than Math.Sign.
s_hewitt
@s_hewitt: My tests shows the inverse.
serhio
+3  A: 

Test Results (Dual core, x86):

''''''''''''''''''''' DEBUG MODE '''
= 1 =
Division took    00:00:06.2482408 ms
BuiltInSign took 00:00:05.0293383 ms <<<
BitTestSign took 00:00:05.2092181 ms
CustomSign took  00:00:05.2512802 ms

= 2 =
Division took    00:00:06.2477787 ms
BuiltInSign took 00:00:05.0330921 ms <<<
BitTestSign took 00:00:05.2114098 ms
CustomSign took  00:00:05.2556966 ms

= 3 =
Division took    00:00:06.2506690 ms
BuiltInSign took 00:00:05.0388615 ms <<<
BitTestSign took 00:00:05.2306954 ms
CustomSign took  00:00:05.2512391 ms


''''''''''''''''''' RELEASE MODE '''
= 1 =
Division took    00:00:01.0974078 ms
BuiltInSign took 00:00:00.3195232 ms
BitTestSign took 00:00:00.6392142 ms
CustomSign took  00:00:00.3194230 ms <<<

= 2 =
Division took    00:00:01.1007138 ms
BuiltInSign took 00:00:00.3197784 ms <<<
BitTestSign took 00:00:00.6395294 ms
CustomSign took  00:00:00.3202774 ms

= 3 =
Division took    00:00:01.0977087 ms
BuiltInSign took 00:00:00.3194622 ms <<<
BitTestSign took 00:00:00.6394220 ms
CustomSign took  00:00:00.3201607 ms

Code:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        Stopwatch sw = new Stopwatch();
        MyEnum myEnum = MyEnum.None;

        const int max = 100000000;

        sw.Start();
        for (int i = -max; i < max; i++)
        {
            myEnum = Division(i);
        }
        sw.Stop();
        Console.WriteLine("Division took {0} ms", sw.Elapsed);
        sw.Reset();

        sw.Start();
        for (int i = -max; i < max; i++)
        {
            myEnum = BuiltInSign(i);
        }
        sw.Stop();
        Console.WriteLine("BuiltInSign took {0} ms", sw.Elapsed);
        sw.Reset();

        sw.Start();
        for (int i = -max; i < max; i++)
        {
            myEnum = BitTestSign(i);
        }
        sw.Stop();
        Console.WriteLine("BitTestSign took {0} ms", sw.Elapsed);
        sw.Reset();

        sw.Start();
        for (int i = -max; i < max; i++)
        {
            myEnum = CustomSign(i);
        }
        sw.Stop();
        Console.WriteLine("CustomSign took {0} ms", sw.Elapsed);
    }

    private MyEnum Division(int value)
    {
        return value == 0 ? 0 : (MyEnum)(value / Math.Abs(value));
    }

    private MyEnum BuiltInSign(int value)
    {
        return (MyEnum)Math.Sign(value);
    }

    private MyEnum CustomSign(int value)
    {
        if (value < 0)
            return MyEnum.Neg;

        if (value > 0)
            return MyEnum.Pos;

        return MyEnum.None;
    }

    MyEnum BitTestSign(int value)
    {
        // Shifts the variable over 31 places, 
        // if the MSB is 1, the statement is true
        if ((value >> 31) == 1)
        {
            return MyEnum.Neg;
        }
        else
        {
            if (value == 0)
            {
                return MyEnum.None;
            }
            else
            {
                return MyEnum.Pos;
            }
        }
    }

    private enum MyEnum
    {
        Pos = 1,
        None = 0,
        Neg = -1
    }
}
serhio
In the CustomSign, why do you `return (MyEnum)(-1)` instead of `return MyEnum.Pos`? Why is `MyEnum.Pos` a negative number, and the opposite from your original question?
GvS
Shouldn't your test set also have negative numbers? Now, you do not measure the perf for negatives, it should be different.
GvS
updated.........
serhio
On my PC, if I run from -max to max the CustomSign is the fastest.
GvS
@GvS update from -max to max, DualCore, x86
serhio
CustomSign is the fastest on my machine too. For -max to max where max = 1000000000. Div = 15s, BuiltInSign = 10s, BitTestSign = 10s, CustomSign = 8s.
s_hewitt
I also modified your test to include as many zeros as negative / positive numbers. After for(-max to max) I added for(0 to max){Division(0);}. Similar results, Division slowest, CustomSign fastest.
s_hewitt
@serhio - are you running your tests in Debug mode instead of Release? I get results like yours in Debug mode. You should try running them in Release mode.
s_hewitt
@s_hewitt: I run in Debug. Is the difference relevant for the subject?
serhio
@serhio - I believe it is relevant. See http://stackoverflow.com/questions/2446027/c-debug-vs-release-performance and the accepted answer's link to Eric Lippert's blog.
s_hewitt
@s_hewitt: Updated the result tests with release mode. Almost no changes.
serhio
And if you add the zeros so there are as many zeros as Negative and Positive numbers?
s_hewitt
+1  A: 

.NET internally stores ints as two's complement. So, if you want to try something else, check and see if the most significant bit is set. This may or may not be faster, but it should fit into your testing framework easily.

Pseudocode:

if(num == 0)
  return 0;

if(num has msb set)
  return -1;

return 1;
Donnie
this method is not optimal, see the test results.
serhio
This method is faster than Math.Sign on my machine.
s_hewitt
@s_hewitt: But on mine, is slower, see result test...
serhio