Sunday, February 15, 2009

Floating point numbers and mysterious roundups...

I'm sure not many Java programmers would've ended up in this situation... Ever tried inserting a floating point number into a database ending up in arbitrary roundups? If so, you know what I'm talking about...

Let's say we need to get the price of some commodity from an input source and insert it into a table. Assuming that the variable is f and it's value is 44.55 (just an example), wouldn't you be surprised to see 44.6 in the database when the code snippet looks something like this?

PreparedStatement pstmt = con.prepareStatement(query);
pstmt.setFloat(f);
System.out.println(f);
pstmt.executeUpdate();

The out.println would print exactly what you expect it to: 44.55; but chances are that this number is rounded off to 44.6 in the database... Simple problem but might sound mysterious too.

Java's float and double classes take the mathematical constant e as their base and hence not every decimal (base 10) floating point number can be represented with the same accuracy in float or double. The spec says floating point numbers are for those values that are fundamentally inaccurate to begin with, like readings of an experiment which can't be precise to 2 decimals and even if it is not precise to 2 decimals chances are that the results aren't altered drastically.

Coming back to our original problem, what is the solution?
The solution is NOT to use floating point or double datatypes.

So what datatype should I use?
BigDecimal.

What on world is that?
That is the decimal that you and I understand - to the base 10.

How to use it?
Pretty straightforward like any other wrapper classes.

PreparedStatement pstmt = con.prepareStatement(query);
pstmt.setBigDecimal(bigDecimal);
pstmt.executeUpdate();

This would solve your problem. Remember, BigDecimal has a lot of overloaded constructors and the simplest I could think of is to use new BigDecimal("12.34"); -- don't forget the quotes. :)

2 comments:

Ram said...

We use BigDecimal all the time at work here. After all, these rounding screw ups are unacceptable when you deal with taxes!

Agni said...

Quite obvious! :) But I never knew that the value can screw up after it is stored. float f = 2.34; Sysout(f) prints 2.34 without any roundoff confusions, but after is set it into a prepared statement which is pretty much like float b = a; I never expected the value to screwup... Whatever it was, I was really irritated and had to change the code to BigDecimal :) Y let a fellow programmer suffer? :D