In contrast to previous installments of the featured function series, this article will cover four functions with identical types, from the same typeclass, that do not do the same thing. They do almost the same thing, what we’re cavalierly describing as “rounding”, but not quite. The four functions are
round :: (RealFrac a, Integral b) => a -> b
truncate :: (RealFrac a, Integral b) => a -> b
ceiling :: (RealFrac a, Integral b) => a -> b
floor :: (RealFrac a, Integral b) => a -> b
They are all methods of the
RealFrac class, and they are documented along with the class documentation.Documentation for the
RealFrac class. They are all also in the standard
Prelude. You may already be familiar with these concepts, and, if so, then the documentation that is already provided for these functions in the
base documentation may suffice for you. But we found in the course of our work that there were a few subtleties that we didn’t quite grasp until we looked at multiple examples.In particular,
truncate was new to us when we first encountered it in the code for the Mandelbrot artwork. We write this now in the hope that such extra documentation will be useful for others.
Each of these functions takes a fractional type of number, such as
Rational, and returns an integral type of number, such as
Int. As such, and given the nature of rounding, the results are always an approximation of the fractional number. Each of these follows a different rule for rounding.
roundis the closest to rounding as you might have learned it in elementary school.
round xreturns the nearest integer to
xis equidistant between two integers, it returns the even one. The strategy of rounding ½ to the closest even number has a number of nicknames including “statistician’s rounding”, because it can avoid a sort of bias that may occur if halves were always rounded up.
truncate xrounds toward zero and therefore returns the nearest integer to
xthat is between zero and
It might be unusual to call
floor “rounding” functions, but they are similar to
truncate in that they also return the nearest integer to some fractional number, but with different rules, and rounding is one of the roles they play.
ceiling xreturns the least integer that is greater than or equal to
floor xreturns the greatest integer that is less than or equal to
round first; it does what you expect it to, so long as you remember that it returns the nearest even number when the given input is halfway between two integers.
λ> round 3.6 4 λ> round (-3.6) -4 λ> map round [3.5, 4.5, 5.5, 6.5] [4,4,6,6]
That is different from truncation.
λ> truncate 3.6 3 λ> truncate (-3.6) -3 λ> map truncate [3.5, 4.5, 5.5, 6.5] [3,4,5,6]
truncate always rounds toward zero,
truncate x is the same as
floor x for positive numbers and the same as
ceiling x for negative numbers. One could define
truncate in terms of
ceiling like this:
To make explicit the metaphor for “floor” and “ceiling”, we should imagine numberedThese floors are numbered with the ground floor as “0”, as many but not all parts of the world do. floors of a building.
Say you are, like the people in the picture, on floor 2. If you point upward toward the ceiling, you are pointing at floor 3. If you look down at the floor, you are seeing floor 2.
λ> map ceiling [2.1, 2.2, 2.6, 2.9] [3,3,3,3] λ> map floor [2.1, 2.2, 2.6, 2.9] [2,2,2,2]
The metaphor also extends further down into the basement. Let’s take a trip down there to the damp negative floors.
We’re now standing on floor negative three. If you reach up to the ceiling you touch floor negative-2. Crouch down to the floor and that’s floor negative-3.
λ> map ceiling [-2.1, -2.2, -2.6, -2.9] [-2,-2,-2,-2] λ> map floor [-2.1, -2.2, -2.6, -2.9] [-3,-3,-3,-3]
The notion of truncation does not fit into this metaphor, at least not pleasantly. There does seem to be a fitting spacial metaphor, but it is over the representations of the numbers. Imagine the numbers are written down, and now the truncator shows up for work.
Truncation is a crude but effective technique performed with a large chainsaw.
λ> map truncate [2.1, 2.2, 2.6, 2.9, -2.1, -2.2, -2.6, -2.9] [2,2,2,2,-2,-2,-2,-2]
The positive numbers get smaller when truncated, but the negative numbers get greater! The truncator does not know why, but they are satisfied with a job well done.