Computing an array's average

exercise No. 222

Q:

We consider a method double averageRoundedOneDecimal(final int[] values):

/**
 * <p>Rounding the sum of values to one decimal place.</p>
 *
 * <p>Example: averageRoundedOneDecimal(new int[]{0, 1, 1}) yields 0.7 rather than 0.66666... .</p>
 *
 * @param values Input values
 * @return The average of all input values rounded to one decimal place.
 */
static double averageRoundedOneDecimal(final int[] values) {
    long sum = 0;
    for (final int value: values) {
        sum += value;
    }
    return Math.round(10 * sum / values.length) / 10;
}
  1. Why is sum being declared as type long despite summing up values from an int array?

  2. The intention is rounding e.g. 3.173 to 3.2. Unfortunately the Javadoc promise does not hold: Executing averageRoundedOneDecimal(new int[]{0, 1, 1}) effectively yields zero rather than 0.7.

    1. Name the culprit.

    2. Provide a fix without changing the method's signature.

Hint: Read the Math.round(...) method's Javadoc.

A:

There are two issues here:

  1. Coding long sum avoids overflow issues when summing up int values.

    1. 10 * sum is an int expression. Dividing by values.length simply cuts off and thus yields zero for the example in question. Since Math.round(...) has got return type long the very same is holds for the final division by 10 as well.

    2. The Java compiler already provides a warning clue related to Math.round(...)'s return type:

      'Math.round(10 * sum / values.length) / 10': integer division in floating-point context

      We possible end up with cutting off at both divisions. Enforcing double division solves these two issues:

      return Math.round(10.0 * sum / values.length) / 10.0;