Float and double in Java "inaccurate" result

There is a well known problem with precise floating point numbers representation in a computer, and Java is no different in this case. If you want to see it for yourself, than try executing the below code:

Double d = new Double(4.395f);
System.out.println(d);

You might expect the 4.395 will be printed out, right? Well, you’re defining an “exact” number as a float (32 bit), than casting it to the double (64 bit) so there should be no problem, as it shouldn’t affect the value.  After all it’s only 4.395 and not 4.3956312123540604, right? In the matter of fact, the result will be like this:

4.394999980926514

Not quite what you expected, huh? Well, going further, if you would try rounding this double value, using something like this:

NumberFormat nf = NumberFormat.getInstance();
    
// It means that 3.125 should be rounded to 3.13, and 3.123 to 3.12
nf.setRoundingMode(RoundingMode.HALF_UP);
nf.setMaximumFractionDigits(2);
    
System.out.println(nf.format(value));

Than you’ll end with a value of

4.39, instead of expected 4.40. And this might cause even bigger problems in the future. So, the advice is – if you are facing a problem of a double <-> float casting, financial operations or any other floating-point sensitive operations, use BigDecimal class. It will save you a lot of time trying to find where is the bug or even more serious consequences of so written code. It saves the number which consists of unscaled integer value (32 bit) and scale integer value (also 32 bit) and gives you a possibility to define the MathContext which affects the held number, as well as the rounding mode. The above code can be rewritten to use the BigDecimal class, as follows:

/** Instantiate the BigDecimal class with given value. Note that
 * there is no BigDecimal(float) constructor, hence the float
 * value will be promoted to the double type and BigDecimal(double)
 * will be executed ultimately.
 */
BigDecimal bd = new BigDecimal(4.395f);   

/** Not setting the Rounding mode will result in throwing
 * ArithemeticException, as the rounding is necessary in this
 * case (remember it's the 4.394999980926514 double value).
 * The BigDecimal is immutable, so this method doesn't affect the
 * original object referenced by <strong>bd</strong> variable.
 */
bd = bd.setScale(3, RoundingMode.HALF_UP);

// When converting, the number represents the actual value.
double res3 = bd.doubleValue();

System.out.println(res3);

The result will be as expected (already rounded):

4.40

Note, that if you would like to get the original value, you could use this:

// Define we are interested in Decimal32 format.
BigDecimal bd = new BigDecimal(4.395f, MathContext.DECIMAL32);

double res3 = bd.doubleValue();

System.out.println(res3);

The result will be:

4.395

Just remember that the DECIMAL32 (or any other constant in MathContext) defines it’s RoundingMode!

You can also take a look at this discussion or take a look at “Effective Java” by Joshua Bloch (Second Edition) where you can read about this problem in Chapter 8, Item 48: Avoid float and double if exact answers are required.