1. Introduction
As we know, rounding makes a number shorter and simpler at the cost of precision.
In this tutorial, we’ll look at some ways in which we can round numbers in Kotlin.
2. Rounding With BigDecimal
The BigDecimal class offers an easy way of rounding Double numbers:
val rawPositive = 0.34444
val roundedUp = rawPositive.toBigDecimal().setScale(1, RoundingMode.UP).toDouble()
assertTrue(roundedUp == 0.4)
With setScale(), we specify the number of decimal places to which the Double must be rounded. To illustrate this, let’s set the scale to 2:
val roundedUp = rawPositive.toBigDecimal().setScale(2, RoundingMode.UP).toDouble()
assertTrue(roundedUp == 0.35)
As shown, when we set the scale to 2, the number is rounded to two decimal places.
Also, we can also specify the rounding mode. For example, using RoundingMode.UP, we can round away from zero.
RoundingMode.HALF_UP is a common way of rounding. This is also the rounding mode typically taught in school.
Likewise, if we set RoundingMode.DOWN, we can round the number down to zero:
val rawPositive = 0.34444
val rawNegative = -0.3444
val roundedDown = rawPositive.toBigDecimal().setScale(1, RoundingMode.DOWN).toDouble()
assertTrue(roundedDown == 0.3)
val roundedDownNegative = rawNegative.toBigDecimal().setScale(1, RoundingMode.DOWN).toDouble()
assertTrue(roundedDownNegative == -0.3)
We set the rounding mode to RoundingMode.DOWN. We also set the scale to 1*.* As expected, the number is rounded down towards zero to a single decimal place.
There are a number of other rounding modes we can use.
For example, RoundingMode.CEILING rounds to positive infinity:
val roundedCeiling = rawPositive.toBigDecimal().setScale(1, RoundingMode.CEILING).toDouble()
assertTrue(roundedCeiling == 0.4)
Similarly, RoundingMode.FLOOR rounds towards negative infinity:
val roundedFloor = rawPositive.toBigDecimal().setScale(1, RoundingMode.FLOOR).toDouble()
assertTrue(roundedFloor == 0.3)
We also have a few more rounding modes:
val roundedHalfUp = 1.55.toBigDecimal().setScale(1, RoundingMode.HALF_UP).toDouble()
assertTrue(roundedHalfUp == 1.6)
val roundedHalfEven = 1.55.toBigDecimal().setScale(1, RoundingMode.HALF_EVEN).toDouble()
assertTrue(roundedHalfEven == 1.6)
val roundedHalfDown = 1.55.toBigDecimal().setScale(1, RoundingMode.HALF_DOWN).toDouble()
assertTrue(roundedHalfDown == 1.5)
All these modes round towards their nearest neighbors. However, the behavior is different when the neighbors are equally distant:
- RoundingMode.HALF_UP rounds up
- RoundingMode.HALF_DOWN rounds down
- RoundingMode.HALF_EVEN towards the nearest even neighbor
3. Using String.format()
We can also use String.format() to round decimal numbers:
val raw1 = 0.34
val raw2 = 0.35
val raw3 = 0.36
val rounded1: Double = String.format("%.1f", raw1).toDouble()
assertTrue(rounded1 == 0.3)
val rounded2: Double = String.format("%.1f", raw2).toDouble()
assertTrue(rounded2 == 0.4)
val rounded3: Double = String.format("%.1f", raw3).toDouble()
assertTrue(rounded3 == 0.4)
Here, the String.format() behaves like RoundingMode.HALF_UP. However, there is no way to modify the rounding mode.
Also, we should consider specifying Locale. By doing so, we avoid errors in non-English locales. Examples of this would be the French or the Slavic locales:
val rounded: Double = String.format("%.1f", raw1).toDouble()
Running the code above when the default locale is, say, Locale.French, would lead to a NumberFormatException. For handling this, we should specify the Locale:
val rounded: Double = String.format(Locale.ENGLISH, "%.1f", raw1).toDouble()
assertTrue(rounded == 0.3)
4. Using DecimalFormat
Similar to String.format(), we can format numbers with DecimalFormat:
val df = DecimalFormat("#.#", DecimalFormatSymbols(Locale.ENGLISH))
val raw1 = 0.34.toBigDecimal()
val raw2 = 0.35.toBigDecimal()
val raw3 = 0.36.toBigDecimal()
val rounded = df.format(raw1).toDouble()
assertTrue(rounded == 0.3)
val rounded = df.format(raw2).toDouble()
assertTrue(rounded == 0.4)
val rounded = df.format(raw3).toDouble()
assertTrue(rounded == 0.4)
Here, RoundingMode.HALF_EVEN is used by default. We can also specify the rounding mode:
val df = DecimalFormat("#.#", DecimalFormatSymbols(Locale.ENGLISH))
df.roundingMode = RoundingMode.FLOOR
val rounded3Floor = df.format(raw3).toDouble()
assertTrue(rounded3Floor == 0.3)
5. Conclusion
To sum it up, we can perform rounding in Kotlin in many ways, although the one we choose may depend on our use case.
As always, the code samples can be found over on GitHub.