Rounding

As mentioned in Chapter 6, integers commonly include more digits of accuracy than needed, and this sometimes leads to the need to truncate numbers. This chapter considers a common alternative to truncation — rounding.

Motivation

In the example from Chapter 6, the worker was paid per piece, but only for multiples of ten pieces. So, the number of pieces manufactured was truncated to the 10s place. Not surprisingly, this policy might make workers unhappy. So, in an effort to improve job satisfaction, the company might decide to round to the 10s place rather than truncate to it.

Review

Under the system in Chapter 6, if an employee manufactured 526 items then they would be paid for 520 items. As you now know, letting number denote the value of interest and place denote the place to truncate to (e.g., 10, 100, 1000, etc.), you can calculate the truncated value using the following pattern:

        truncated = (number / place) * place;


Given the original value and the truncated value, it shouldn’t be too difficult to find the rounded value. All that’s needed is a way to determine if the rounded value is larger than the truncated value or not. If it is, then an appropriate adjustment must be added to the truncated value.

Returning to the manufacturing example, the truncated value is 520. What about the rounded value? Since the actual number of items is 526, and 526 is at least halfway between 520 and 530, the rounded value should be

1. Hence, the truncated value needs to be adjusted by 10 to get the rounded value.

Going from this specific example to a more general solution requires two observations. First, if the rounded value is larger than the truncated value, it is larger by exactly the amount place (i.e., $$10$$ in the example). Second, to determine if the rounded value should be larger than the truncated value, you need to compare the difference between the number and the truncated value (i.e., $$6$$ in the example), which is also the remainder after division with half of the amount place.

The Pattern

So, given the truncated value as calculated above, you can calculate the rounded value as follows:

        if ((number % place) >= (place / 2)) {
rounded = truncated + place;
} else {
rounded = truncated;
}


Note that, since place will always be a power of ten, no complications arise when dividing it (an integer) by 2 (another integer). In other words, place is always evenly divisible by 2.

Putting it all together, you can write a method like the following for solving general rounding problems.

    public static int round(int number, int place) {
int rounded, truncated;

truncated = (number / place) * place;

if ((number % place) >= (place / 2)) {
rounded = truncated + place;
} else {
rounded = truncated;
}

return rounded;
}


In essence, this pattern is a combination of the truncation pattern from Chapter 6 and a threshold indicator from Chapter 8. It is also a good example of how patterns can be combined to solve other problems.

Examples

Returning to the manufacturing example, given an int variable named items, you can determine how much an employee that produces 526 items should be paid by calculating the rounded number of items as follows:

        items = 526;
place = 10;
rounded = round(items, place);


As another example, suppose you want to talk about something that will happen 93 years after the year 1993. You might want to be exact and use the year 2086, but you also might want to round to the nearest decade or century. Using the rounding pattern, this can be accomplished as follows:

        exact   = (1993 + 93);
century = round(exact, 100);


In the first invocation, the value of truncated will be 2080, the value of number % place will be 6 (which is greater than place / 2 which is 5), so the value of rounded will be 2090. In the second invocation, the value of truncated will be 2000, the value of number % place will be 86 (which is greater than place / 2 which is 50), so the value of rounded will be 2100.

A Warning

As mentioned in Chapter 6 on the truncation pattern, it is important to distinguish between the numerical accuracy that should be used when performing calculations and the accuracy (or format) used when displaying output. It is your responsibility to know what is required of a particular section of code.

As mentioned in Chapter 9 in the discussion of threshold indicators, on some kinds of hardware, these kinds of calculations sometimes need to be performed using arithmetic operators only (i.e., without the use of if statements). Fortunately, doing so is not very difficult. To understand how, it is easiest to build up to the general case from some specific cases.

If you need to round to the 10s place then you need to know if the remainder is greater than or equal to the threshold of 5, in which case you should increase the truncated value by 10. Otherwise, you shouldn’t increase it (or you should increase it by 0). In this case, since remainder % 5 is 1 when remainder is greater than or equal to the threshold of 5 and is 0 otherwise, you can write the increase as:

        increase = (remainder % 5) * 10;


If you need to round to the 100s place, however, things are a little more complicated because then the question is not whether the remainder is greater than or equal to 5, but whether the remainder is greater than or equal to the threshold of 50. This means that, if the threshold is calculated as follows:

        threshold = 5 * (100 / 10);


then threshold is less than 2 * value, and you can use the simplest arithmetic threshold indicator from Chapter 9. In particular:

        indicator = remainder / threshold;


You can then calculate the increase as follows.

        increase = indicator * 100;


Letting place denote the place being rounded to (i.e., 10 for the 10s place, 100 for the 100s place, etc.) this can be generalized as follows:

        truncated = number / place;
remainder = number % place;
threshold = 5 * (place / 10);
indicator = remainder / threshold;
increase = indicator * place;
rounded = truncated + increase;


where all of the variables are integers.