4

If you run the following code:

SByte w = -5;
Console.WriteLine(w.CompareTo(0));

Int32 x = -5;
Console.WriteLine(x.CompareTo(0));

SByte y = 5;
Console.WriteLine(y.CompareTo(0));

Int32 z = 5;
Console.WriteLine(z.CompareTo(0));

then you get the following output:

-5
-1
5
1

Why do these methods with the same name that have almost identical descriptions in the MSDN documentation behave so differently?

4
  • 1
    In what way are they behaving differently? It's only the sign of the result that matters. -5 and -1 have the same sign, and 5 and 1 have the same sign. In other words, as far as the documented behaviour is concerned, they're behaving equivalently.
    – Jon Skeet
    Commented Apr 5, 2016 at 12:32
  • 2
    SByte - SByte can not overflow Int32, but Int32 - Int32 can. Commented Apr 5, 2016 at 12:34
  • The IComparable interface is defined as having to return a value > 0, 0 or < 0. It does not specify which values these have to be exactly. Most people use 1, 0 and -1 but any other value is perfectly allowed as well. The only thing that matters are the signs for > and < and 0 for equal. Commented Apr 5, 2016 at 12:34
  • IComparable or IComparable<T> are used for ordering, so it's only important to know whether two objects are equal(zero), one is less- or greater than zero. What value is returned depends on the implementation and is irrelevant. Commented Apr 5, 2016 at 12:35

2 Answers 2

12

Because the SByte.CompareTo() is implemented like

return m_value - value;

so a simple subtraction. This works because the m_value is converted automatically to int, and any possibile combination of values is "legal" with int.

With two Int32 this can't be done, because for example Int32.MinValue.CompareTo(Int32.MaxValue) would become Int32.MinValue - Int32.MaxValue that would be outside the int range, and in fact it is implemented as two comparisons:

if (m_value < value) return -1;
if (m_value > value) return 1;
return 0;

in general

The only important "thing" of the returned value of a CompareTo is its sign (or if it is 0). The "value" is irrelevant. The return value of 1, 5, 500, 5000, 5000000 of CompareTo() are the same. CompareTo can't be used to measure "distance" between numbers. So both implementations are equivalent.

It is totally wrong to do:

if (someValue.CompareTo(someOtherValue) == -1)

you must always

if (someValue.CompareTo(someOtherValue) < 0)

why the SByte.CompareTo is built that way

SByte.CompareTo is implementing a "branchless" comparison (there are no ifs in the code, the flow of code is linear). Processors have problems with branches, so branchless code could be faster than "branchful" code, so this microoptimization. Clearly SByte.CompareTo could have been written as Int32.CompareTo.

why any negative value is equivalent to -1 (and any positive value is equivalent to +1)

This is probably something that is derived directly from the C language: the qsort function for example to compare items uses a user-defined method that is like:

Pointer to a function that compares two elements.
This function is called repeatedly by qsort to compare two elements. It shall follow the following prototype:

int compar (const void* p1, const void* p2);

Taking two pointers as arguments (both converted to const void*). The function defines the order of the elements by returning (in a stable and transitive manner):
return value meaning
<0 The element pointed to by p1 goes before the element pointed to by p2
0 The element pointed to by p1 is equivalent to the element pointed to by p2
>0 The element pointed to by p1 goes after the element pointed to by p2

how is the .CompareTo implemented in other primitive types?

SByte, Byte, Int16, UInt16, Char all use the subtraction "method", while Int32, UInt32, Int64, UInt64 all use the if "method".

3
  • 2
    You mean CompareTo, not GetHashCode.
    – Jon Skeet
    Commented Apr 5, 2016 at 12:36
  • That's the implementation. But does it matter at all? Isn't the real question why CompareTo can return other values than 0, -1 and 1. OP doesn't seem to know for what it's normally used: ordering. Commented Apr 5, 2016 at 12:41
  • @TimSchmelter You are right... I've added some annotations... I hadn't written them because I'm not an expert in CS, so I'm not sure if what I've written is history or more hearsay
    – xanatos
    Commented Apr 5, 2016 at 12:52
6

Looking at the source for these two methods, they are implemented differently:

public int CompareTo(sbyte value)
{
    return (int)(this - value);
}

vs

public int CompareTo(int value)
{
    if (this < value)
    {
        return -1;
    }

    if (this > value)
    {
        return 1;
    }

    return 0;
 }

But none of this matters, since the sign of the returned value is the only thing that you should be checking.

0

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