A logic explanation about averages

Hello, we are writing a homework code about finding the average of odd numbers in an array. Someone helped us a little with the code, but I can't figure out logically why what he mentioned works. When I manually calculate the odd numbers, I just divide by the count and it works. Is it for decimal places?
Thanks



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 float averageOddNumbers(int arr[],int n)//calculate odd numbers Average of array
{
int count=0;
int sum=0;
int i=0;
for(i=0;i<n;i++)
if(arr[i]%2==1) //if odd number
{
sum=sum+arr[i];
count++;  //to count how many numbers are in the array 
}
return(sum*1.0)/count;
}

outfile<<"Average of odd numbers in array A is:";  //finding the average of odd numbers in A 
avgOddNumbers=averageOddNumbers(A,25);
outfile<<avgOddNumbers<<" "<<endl;


Average of odd numbers in array A is:45.9

without the 1.0= Average of odd numbers in array A: 45.0
integer math: 3/4 = 0. float/double math: 3/4 = 0.75 or thereabouts.
because count and sum are integers, you are doing integer math when you divide sum by count.
the 1.0 is a obscure way to fix the problem.
more often you see a cast: ((double)sum)/count or the like.
or you could make sum a double to begin with.

do not use float. use double. floats lack the precision to do much beyond the crudest schoolwork and are only used in the real world on rare occasions to save a few bytes (over billions of numbers it can add up) when the precision allows for it (rare).
There are two things happening here which are getting confused.

The mean (or average) is always computed the same way, regardless of how many items there are:

    (sum of all elements) / (number of elements)


The code example above is also filtering the array. It is only finding the average of the elements of the array that are odd numbers, and ignoring those elements that are not odd.

For example, given the array:

    { 1, 2, 3, 4, 5, 6 }

We see that there are six elements. The average is (1+2+3+4+5+6)/6 = 3.5.

However, if we only want the odd elements, our array is effectively changed to:

    { 1, 3, 5 }

Three elements. Average = (1+3+5)/3 = 3.0.


This filter step could be done separately, but the two algorithms are combined to work together.

1
2
3
4
5
6
7
8
9
10
11
  for (i=0; i<n; i++)  // loop through EVERY item in the array
  {
    if (arr[i]%2==1)  // but only continue if the item is ODD
    {
      sum=sum+arr[i];  // update our sum (with the odd number)
      count++;  // update our count (of odd numbers)
    }
  }

  // the average is the (sum of the odd numbers) / (count of odd numbers)
  return sum*1.0/count;


That last line also needs a little explanation. In C and C++, the solidus (division operator) works over different types. If both types are integers, the result of the division is also an integer (you get integer division). Hence, 21/6 is exactly 3, an integer.

However, if at least one of the operands is a floating point type, the result of the division is also floating point. Hence, 21.0/6 is exactly 3.5, a float.

Your function returns a float, so regardless of whether you perform integer or floating-point division you get a float, which is why you get 45.0 as a result instead of the integer 45.

To force one of the operands to be float, simply multiply a floating value in. The identity value 1.0 is a good one.


As a matter of technical detail, since the function returns float and not double, that 1.0 should be 1.0f, indicating a float and not a double.

12
13
  return (sum*1.0f)/count;
}

Hope this helps.

[edit]
Fixed stupid typos.
And autocorrects.
Last edited on
@jonnin
There are plenty of very old, very correct scientific applications that work over float.

The choice between the two should depend more on your constraints.

For general use, though, it is ok to just go for double.


BTW, if you plan to do any kind of number crunching, the following is required reading:
What Every Computer Scientist Should Know About Floating-Point Arithmetic
https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

Hope this helps.
I know. But writing new code, I would still advise to stick to doubles unless you have a good reason not to. If you have a good reason, float is there for you. Some microdevices also still spew floats, eg cheap gps (and cheap is good, if it works!)
Last edited on
Thanks guys! This is an introductory class and some rules are not blatantly obvious. I really appreciate it.
@jonnin
Yes, agreed that new code is typically fine with doubles.

@XboxOne2019
Remember, nothing is obvious until it has been explained. People who pretend otherwise are pretentious jerks.

The 4/5 integer division thing is a quirk that trips up a lot of programmers new to C-like languages. (Contrast, say, ADA-like languages, which have two different names for integer and floating-point division (div and /, respectively).)


It is one thing to keep in mind for future C++ stuff as well. When overloading functions, the choice depends on the type(s) of the arguments. This is what makes overloading operators so convenient:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <numeric>

struct rational
{
  int numerator;
  int denominator;

  rational( int numerator = 0, int denominator = 1 )
  : numerator{numerator}, denominator{denominator}
  { }
};

rational operator / ( const rational& a, const rational& b )
{
  return rational{
    a.numerator   * b.denominator,
    a.denominator * b.numerator
  };
}

std::ostream& operator << ( std::ostream& outs, const rational& r )
{
  return outs << r.numerator << "/" << r.denominator;
}

Here, we have created a new type (a ‘rational’ value), with the ability to divide one rational by another. Since the new division operator only works on rationals, the division operator for other types is not affected.

1
2
3
4
5
6
7
8
9
10
int main()
{
  int a = 12;
  int b = 5;

  rational c{ 1, 3 };
  rational d{ 2, 8 };

  std::cout << a << " / " << b << " = " << (a / b) << "\n";  // integer division
  std::cout << c << " / " << d << " = " << (c / d) << "\n";  // rational division 

And the output is as expected: integer division 12/5 = 2, rational division (1/3) / (1/4) == (1/3) * (4/1) == 4/3.
$ clang++ -std=c++17 -O3 -Wall -pedantic-errors -I . a.cpp rational.cpp

$ a
12 / 5 = 2
1/3 / 1/4 = 4/3

$ _

Enjoy!

Beware, that rational type is exceedingly simple, and only does enough to show an example of overriding the division operator to work over a new type of thing.
I dunno if this is off the rails stuff or not. But I wanted to write it, so there.
Topic archived. No new replies allowed.