4

There are already solutions to this problem for small numbers:

I'll summarise the answer to them all:

Math.Abs(a - b)

The problem is when the numbers are large this gives the wrong answer (by means of an overflow). Worse still, if (a - b) = Int32.MinValue then Math.Abs crashes with an exception (because Int32.MaxValue = Int32.MinValue - 1):

System.OverflowException occurred
HResult=0x80131516
Message=Negating the minimum value of a twos complement number is invalid.
Source=mscorlib
StackTrace: at System.Math.AbsHelper(Int32 value) at System.Math.Abs(Int32 value)

Its specific nature leads to difficult-to-reproduce bugs.

Maybe I'm missing some well known library function, but is there any way of determining the difference safely?

RBT
  • 24,161
  • 21
  • 159
  • 240
c z
  • 7,726
  • 3
  • 46
  • 59
  • 7
    BigInteger - https://msdn.microsoft.com/en-us/library/system.numerics.biginteger(v=vs.110).aspx – Robert Rouhani Aug 01 '16 at 15:20
  • 5
    What about converting to a long (Int64) and then finding the difference? – Igor Aug 01 '16 at 15:20
  • Are a and b Int32 or Int64? – Liam Aug 01 '16 at 15:22
  • Could be anything. Same problem for Int64.MinValue. I guess converting to the higher type and checking is the way to go though, thanks. – c z Aug 01 '16 at 15:24
  • 1
    to be clear - Math.Abs throws in the MinValue case. Its explicily coded indicating that the twos complement of x8000000 is not defined. 'crashing' suggests that this is a bug in the library – pm100 Aug 01 '16 at 15:38

4 Answers4

4

As suggested by others, use BigInteger as defined in System.Numerics (you'll have to include the namespace in Visual Studio) Then you can just do:

BigInteger a = new BigInteger();
BigInteger b = new BigInteger();
// Assign values to a and b somewhere in here...
// Then just use included BigInteger.Abs method
BigInteger result = BigInteger.Abs(a - b);

Jeremy Thompson's answer is still valid, but note that the BigInteger namespace includes an absolute value method, so there shouldn't be any need for special logic. Also, Math.Abs expects a decimal, so it will give you grief if you try to pass in a BigInteger.

Keep in mind there are caveats to using BigIntegers. If you have a ludicrously large number, C# will try to allocate memory for it, and you may run into out of memory exceptions. On the flip side, BigIntegers are great because the amount of memory allotted to them is dynamically changed as the number gets larger.

Check out the microsoft reference here for more info: https://msdn.microsoft.com/en-us/library/system.numerics.biginteger(v=vs.110).aspx

Verbal Kint
  • 425
  • 1
  • 3
  • 20
2

The question is, how do you want to hold the difference between two large numbers? If you're calculating the difference between two signed long (64-bit) integers, for example, and the difference will not fit into a signed long integer, how do you intend to store it?

long    a = +(1 << 62) + 1000;
long    b = -(1 << 62);

long dif = a - b;    // Overflow, bit truncation

The difference between a and b is wider than 64 bits, so when it's stored into a long integer, its high-order bits are truncated, and you get a strange value for dif.

In other words, you cannot store all possible differences between signed integer values of a given width into a signed integer of the same width. (You can only store half of all of the possible values; the other half require an extra bit.)

Your options are to either use a wider type to hold the difference (which won't help you if you're already using the widest long integer type), or to use a different arithmetic type. If you need at least 64 signed bits of precision, you'll probably need to use BigInteger.

David R Tribble
  • 11,918
  • 5
  • 42
  • 52
1

The BigInteger was introduced in .Net 4.0.

There are some open source implementations available in lower versions of the .Net Framework, however you'd be wise to go with the standard.

If the Math.Abs still gives you grief you can implement the function yourself; if the number is negative (a - b < 0) simply trim the negative symbol so its unsigned.

Also, have you tried using Doubles? They hold much larger values.

Jeremy Thompson
  • 61,933
  • 36
  • 195
  • 321
1

Here's an alternative that might be interesting to you, but is very much within the confines of a particular int size. This example uses Int32, and uses bitwise operators to accomplish the difference and then the absolute value. This implementation is tolerant of your scenario where a - b equals the min int value, it naturally returns the min int value (not much else you can do, without casting things to the a larger data type). I don't think this is as good an answer as using BigInteger, but it is fun to play with if nothing else:

    static int diff(int a, int b)
    {
        int xorResult = (a ^ b);
        int diff = (a & xorResult) - (b & xorResult);
        return (diff + (diff >> 31)) ^ (diff >> 31);
    }

Here are some cases I ran it through to play with the behavior:

        Console.WriteLine(diff(13, 14)); // 1
        Console.WriteLine(diff(11, 9)); // 2
        Console.WriteLine(diff(5002000, 2346728)); // 2655272
        Console.WriteLine(diff(int.MinValue, 0)); // Should be 2147483648, but int data type can't go that large. Actual result will be -2147483648.
mars-red
  • 331
  • 2
  • 13