Books / Patterns for Beginning Programmers / Chapter 10
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.
In this chapter
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;
Thinking About The Problem
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
- 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);
decade = round(exact, 10);
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.
Looking Ahead
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.