Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the right way to find the average of two values?

I recently learned that integer overflow is an undefined behavior in C (side question - is it also UB in C++?)

Often in C programming you need to find the average of two values a and b. However doing (a+b)/2 can result in overflow and undefined behavior.

So my question is - what is the right way to find the average of two values a and b in C?

like image 396
bodacydo Avatar asked Jul 23 '14 20:07

bodacydo


People also ask

What is the average of two values?

The mean is what you typically think as the average - found by sum all values and dividing the sum by the number of values. The median is the middle value of the set (or the average of the two middle values if the set is even).

What are different ways to find the average?

There are three main types of average: mean, median and mode. Each of these techniques works slightly differently and often results in slightly different typical values. The mean is the most commonly used average. To get the mean value, you add up all the values and divide this total by the number of values.


1 Answers

With help from Secure Coding

if (((si_b > 0) && (si_a > (INT_MAX - si_b))) ||
    ((si_b < 0) && (si_a < (INT_MIN - si_b))))
{
  /* will overflow, so use difference method */
  return si_b + (si_a - si_b) / 2;
} 
else
{
 /* the addition will not overflow */
  return (si_a + si_b) / 2;
}

ADDENDUM

Thanks to @chux for pointing out the rounding problem. Here's a version that's tested for correct rounding...

int avgnoov (int si_a, int si_b)
{
    if ((si_b > 0) && (si_a > (INT_MAX - si_b)))
    {
      /* will overflow, so use difference method */
      /* both si_a and si_b > 0; 
          we want difference also > 0
          so rounding works correctly */
      if (si_a >= si_b)
        return si_b + (si_a - si_b) / 2;
      else
        return si_a + (si_b - si_a) / 2;
    } 
    else if ((si_b < 0) && (si_a < (INT_MIN - si_b)))
    {
      /* will overflow, so use difference method */
      /* both si_a and si_b < 0; 
          we want difference also < 0
          so rounding works correctly */
      if (si_a <= si_b)
        return si_b + (si_a - si_b) / 2;
      else
        return si_a + (si_b - si_a) / 2;
    }
    else
    {
     /* the addition will not overflow */
      return (si_a + si_b) / 2;
    }
}
like image 123
Doug Currie Avatar answered Oct 07 '22 18:10

Doug Currie