2

Suppose I want to add 1+11+111....adding n times. It is clear that from a certatin value of n, there may be an overflow of the cummulative sum.

Suppose I use the following very simple function to calcuate the sum above:

int calcSum(int num)
{
    int sum = 0, sequnt = 1, i;

    for (i = 0; i < num; i++)
    {
        sum += sequnt;
        sequnt = (sequnt * 10) + 1;
    }

    return sum;
}

To that function I want to add a check for overflowing.

I have tried to get some help here How to check if a number overflows an 'int'

but I have to admit it made me confused, and I still find some difficulties with implementing it in my task.

Any help would be appreaciated.

DonaldT
  • 103
  • 8
  • `if(INT_MAX - sequnt >= sum)` it is safe to do `sum += sequnt;` – Weather Vane Aug 06 '18 at 18:31
  • `INT_MAX` should be your friend – Support Ukraine Aug 06 '18 at 18:31
  • @WeatherVane That is not true for all events. Suppose for 16bit systems a call of calcSum(111111) will overflow to -19961 at function invocation. num will have a value of -19961 and because of integer promotion INT_MAX - -19961 will always be larger than sum. – Kenneth Aug 07 '18 at 07:30
  • @Kenneth 111111 is not a 16-bit value. – Weather Vane Aug 07 '18 at 07:33
  • @WeatherVane It is a perfectly valid 16-bit long integer type. – Kenneth Aug 07 '18 at 07:35
  • @Kenneth the range of a 16-bit `int` is -32768 to 32767. Passing `111111` to a 16-bit `int` does not "overflow" to anything - it is *undefined behaviour*. Also there is no such thing as a 16-bit long integer type, which must be at least 32 bits. – Weather Vane Aug 07 '18 at 07:36
  • @WeatherVane It is legal in C to call a function with a different integer type than specified by its signature. Integer litterals are interpreted at compile time to the smallest possible integer type which fits their value. Therefor an integer litteral of 11111 is interpreted as an int while 111111 is interpreted as a long. So calcSum(11111) is called with an int while calcSum(111111) is called with a long. You will get a waring for the latter call but it is legal. – Kenneth Aug 07 '18 at 07:43
  • @Kenneth I don't see your point, which seems to be that something doesn't work if you pass an out of range value. Garbage in, garbage out. – Weather Vane Aug 07 '18 at 07:48
  • @WeatherVane Pardon the 16-bit long integer type. On that point you are correct. There is a long type for 16-bit systems which is 32-bits. But you can't just say garbage in, garbage out. Technically you are correct. My point is simply that you need to be aware of that garbage especially when passing variables to a function which have a different type. Or writing function calls manually using integer litterals. If you are not careful this will bite you at some point. – Kenneth Aug 07 '18 at 07:51
  • Well, there was a compiler warning. Don't issue code with compiler warnings. – Weather Vane Aug 07 '18 at 07:55
  • @WeatherVane No. No, you cannot rely on that. If you call with an integer litteral there is a warning, like I said earlier. If you call with a long variable there isn't. Not even if you use -Wall. – Kenneth Aug 07 '18 at 08:01

3 Answers3

3

Simply use INT_MAX from limits.h

int calcSum(int num)
{
    int sum = 0, sequnt = 1, i;

    for (i = 0; i < num; i++)
    {
        if (INT_MAX - sequnt < sum) exit(1); // overflow
        sum += sequnt;
        if (INT_MAX/10 <= sequnt) exit(1); // overflow on the two next sentences.
        sequnt *= 10;
        sequnt++;
    }

    return sum;
}

The exit(1) is just to make the example short. You can add whatever error handling that you like.

Luis Colorado
  • 10,974
  • 1
  • 16
  • 31
Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
  • I don't understand the need of checking against `INT_MAX/10` or `INT_MAX - 1`. Can you explain the need to check for that and not for the signs of the two operands? Do you know that if operands have same sign overflow is impossible? What question are you trying to answer, because neither your code nor your explanation answers anything at all. – Luis Colorado Aug 07 '18 at 07:22
  • 1
    @LuisColorado Quote: "Do you know that if operands have same sign overflow is impossible?" What are you talking about? That statement is completely wrong. – Support Ukraine Aug 07 '18 at 07:44
  • Sorry, I made a mistake on writing and tried to say _if two operands have different signs, then adding overflow is impossible_ instead..... See my answer, that has nothing about `INT_MAX/10` or `INT_MAX - 1`. – Luis Colorado Aug 07 '18 at 08:08
  • @LuisColorado I think you have misunderstood this completely. a) OP is not using any negative values so no need for discussing different signs. b) The multiplication may overflow as well so that has to be checked. All operation has be checked. Not just the add to `sum` – Support Ukraine Aug 07 '18 at 08:19
  • The code shows those errors also, but the question title is *only* about integer overflows _in cumulative_ sums. It doesn't speak about multiplication anyway. I agree with you in that way. – Luis Colorado Aug 07 '18 at 08:29
  • I cannot take the downvote off unless I edit the answer, so let me edit it to change `if (INT_MAX/10 < sequnt) exit(1);` to `if (INT_MAX/10 <= sequnt) exit(1);` to refund the two last tests into one (both can be done at once), and you'll get again the lost reputation. – Luis Colorado Aug 07 '18 at 08:35
3

As either of the 2 additions or multiplication are roughly overflow candidates, call a safe overflow checker for each. Use constants in <limits.h> to guide range checking.

#include <limits.h>
int is_undefined_add(int a, int b) {
  return (a < 0) ? (b < INT_MIN - a) : (b > INT_MAX - a);
}

int is_undefined_mult(int a, int b) {
  if (a > 0) {
    if (b > 0) {
      return a > INT_MAX / b;       // a positive, b positive
    }
    return b < INT_MIN / a;         // a positive, b not positive
  }
  if (b > 0) {
    return a < INT_MIN / b;         // a not positive, b positive
  }
  return a != 0 && b < INT_MAX / a; // a not positive, b not positive
}

int calcSum(int num) {
  int sum = 0, sequnt = 1, i;

  for (i = 0; i < num; i++) {
    if (is_undefined_add(sum, sequnt) Handle_Overflow();
    sum += sequnt;

    if (is_undefined_mult(sequnt, 10) Handle_Overflow();
    sequnt *= 10;

    if (is_undefined_add(sequnt, 1) Handle_Overflow();
    sequnt++;
  }

  return sum;
}

is_undefined_add() and is_undefined_mult() are valid for all combinations of int a, int b.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
1

If you are sure you are using two's complement signed integers, then you can check the following: Two positive numbers added must give a positive result and two negative numbers added must give a negative result. It's impossible in only one sum to get two overflows, so if you are adding positive numbers, an overflow will arrive when your result is negative for the first time.

Either case, if you want your code to be portable, then the recommendation is to do something similar to this:

#include <limits.h>
...
    if ((a > 0 && b > 0 && MAX_INT - a > b) ||
        (a < 0 && b < 0 && MIN_INT - a < b)) {
        /* OVERFLOW OF a + b WILL OCCUR */
        ...
    }

if signs of operands are different it is impossible for a + b to be greater than a or b in absolute value, so it is impossible for an overflow to happen.

For unsigneds you have a similar approach (while you save half of the test, as operands can be only positive) but this time the first way is valid always (as standard says unsigned addition is considered a sum module 2^n where n is the wordsize in bits) and in that case you can make the sum and later check if the result is less than any of the operands (if both are positive, sum must be larger than or equal than any of the operands):

unsigned a, b;

...
unsigned sum = a + b;
if (sum < a || sum < b) {
    /* OVERFLOW ON a + b HAS HAPPENED */
    ...
}

You have also to check for integer multiplication overflow. If a and b are to be multiplied, then a*b can overflow. But this time the problem goes further, as overflow can occur more than once, and you cannot do it a posteriori. In that case you can have overflow with equal or different signs, as you are adding a times b (and b has the same sign as itself) if both signs are equal the product will be positive, and overflows will occur

if (MAX_INT/b < a) { /* overflow */ }

if signs are different, the product should be negative, and then

if (MIN_INT/b < a) { /* overflow */ }

if one of the numbers is 0, then no overflow occurs on multipliying, as the result is 0.

Luis Colorado
  • 10,974
  • 1
  • 16
  • 31
  • "if you are adding positive numbers, an overflow will arrive when your result is negative for the first time" It may be that way on some platforms but from a standard point of view it's wrong. When the overflow happens the behavior is undefined from a standard point of view and using the result is meaningless – Support Ukraine Aug 07 '18 at 08:15
  • Sorry, my apologies, I could assure I'd read somewhere. Response edited. – Luis Colorado Aug 07 '18 at 08:26