Over a million developers have joined DZone.

Fixing Floating-Point Arithmetics With Kotlin

Those of you who learned Java in an academic context probably remember something fishy around FP arithmetics. Then if you never used them, you probably forgot about them.

· Java Zone

Check out this 8-step guide to see how you can increase your productivity by skipping slow application redeploys and by implementing application profiling, as you code! Brought to you in partnership with ZeroTurnaround.

This week saw me finally taking time to analyze our code base with Sonar. In particular, I was made aware of plenty of issues regarding floating-point arithmetics.

Fun With Java’s Floating-Point Arithmetics

Those of you who learned Java in an academic context probably remember something fishy around FP arithmetics. Then if you never used them, you probably forgot about them. Here’s a very quick example of interesting it turns out to be:

double a = 5.8d;
double b = 5.6d;
double sub = a - b;
assertThat(sub).isEqualTo(0.2d);

Contrary to common sense, this snippet throws an AssertionError: sub is not equal to 0.2 but to 0.20000000000000018.

BigDecimal as a Crutch

Of course, no language worthy of the name could let that stand. BigDecimal is Java’s answer:

The BigDecimal class provides operations for arithmetic, scale manipulation, rounding, comparison, hashing, and format conversion.

Let’s update the above snippet with BigDecimal:

BigDecimal a = new BigDecimal(5.8d);
BigDecimal b = new BigDecimal(5.6d);
BigDecimal sub = a.subtract(b);
assertThat(sub).isEqualTo(new BigDecimal(0.2d));

And run the test again… Oooops, it still fails:

java.lang.AssertionError: 
Expecting:
 <0.20000000000000017763568394002504646778106689453125>
to be equal to:
 <0.200000000000000011102230246251565404236316680908203125>
but was not.

Using constructors changes nothing, one has to use the static valueOf() method instead.

BigDecimal a = BigDecimal.valueOf(5.8d);
BigDecimal b = BigDecimal.valueOf(5.6d);
BigDecimal sub = a.subtract(b);
assertThat(sub).isEqualTo(BigDecimal.valueOf(0.2d));

Finally it works, but as the cost of a lot of ceremony…

Kotlin to the Rescue

Just porting the code to Kotlin only marginally improves the readability:

val a = BigDecimal.valueOf(5.8)
val b = BigDecimal.valueOf(5.6)
val sub = a.subtract(b)
assertThat(sub).isEqualTo(BigDecimal.valueOf(0.2))

Note that in Kotlin, floating-point numbers are doubles by default.

In order to make the API more fluent and thus the code more readable, two valuable Kotlin features can be applied.

The first one is extension method (I’ve already showed their use in a former post to improve logging with SLF4J). Let’s use it here to easily create BigDecimal objects from Double:

fun Double.toBigDecimal(): BigDecimal = BigDecimal.valueOf(this)

val a = 5.8.toBigDecimal() // Now a is a BigDecimal

The second feature - coupled with method extension, is operator overloading. Kotlin sits between Java where operator overloading is impossible, and Scala where every operator can be overloaded (I’m wondering why there aren’t already any emoticons library): only some operators can be overloaded, including those from arithmetics - +, -, * and /.

They can be overridden quite easily, as shown here:

operator fun BigDecimal.plus(a: BigDecimal) = this.add(a)
operator fun BigDecimal.minus(a: BigDecimal) = this.subtract(a)
operator fun BigDecimal.times(a: BigDecimal) = this.multiply(a)
operator fun BigDecimal.div(a: BigDecimal) = this.divide(a)

val sub = a - b

Note this is already taken care of in Kotlin’s stdlib.

The original snippet can now be written like this:

val a = 5.8.toBigDecimal()
val b = 5.6.toBigDecimal()
assertThat(a - b).isEqualTo(0.2.toBigDecimal())

The assertion line can probably be improved further. Possible solutions include AssertJ custom assertions or… extension method again, in order for the isEqualTo() method to accept Double parameters.

Conclusion

Any complex API or library can be made easier to read by using Kotlin extension methods. What are you waiting for?

The Java Zone is brought to you in partnership with ZeroTurnaround. Check out this 8-step guide to see how you can increase your productivity by skipping slow application redeploys and by implementing application profiling, as you code!

Topics:
api ,kotlin

Published at DZone with permission of Nicolas Frankel, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}