Java | Programming | TestingZero ToleranceNovember 6, 2014Neil Chaudhuri (He/Him)
LinkedIn
Neil Chaudhuri (He/Him)
Experienced Java developers know that Java 5 brought many significant improvements to the language.
One of them was the introduction of BigDecimal, an
abstraction for arbitrary-precision decimal numbers that gives you arithmetic functionality and complete control over
rounding and scale. All of this flexibility and power made BigDecimal
the de facto class Java developers used to model money--at
least until Java 7 gave us the Currency class.
On a recent project though with an API that doesn’t use Currency
, I had a simple question:
How do you test if a BigDecimal is 0?
It turns out that the power of BigDecimal
makes this question a lot trickier than you might think.
My first instinct was to do what you are always supposed to do with equality of objects--use the equals method. So I used BigDecimal’s ZERO constant and did this.
if (price.equals(BigDecimal.ZERO) { //Do stuff }
We do this kind of thing all the time without a second thought. The problem was that my unit test failed, and it turns
out it was because the condition always evaluated to false
--even when price
was definitely 0.
It turns out zero isn’t always zero. At least not when it comes to BigDecimal
.
The answer is in BigDecimal
’s implementation of the equals
method.
Unlike compareTo, this method considers two BigDecimal objects equal only if they are equal in value and scale (thus 2.0 is not equal to 2.00 when compared by this method).
I assumed equals
just compared values, but scale matters. BigDecimal.ZERO
has scale of 0, but the zero value in price
in my test did not.
That’s why things went wrong.
What’s the right way to check if your BigDecimal instance is 0? It turns out there are two right ways.
One of them makes perfect sense if you know Comparable. (If you don’t, check out our tutorial on the topic.)
if (price.compareTo(BigDecimal.ZERO) == 0) { //Do stuff }
This approach is intuitive and readable.
If you want to sacrifice readability to gain a few milliseconds of performance and earn some cuteness points, you can do this.
if (price.signum() == 0) { //Do stuff }
BigDecimal, like all values in Java, represents a signed number, and the signum
method returns the sign of the underlying number. Technically, signum
doesn’t
test the numerical value, but since a BigDecimal representing 0 always has a signum of 0...it works out the same in your condition.
The next time you are checking if your BigDecimal is 0, just remember. It depends on what the meaning of the word of is is.