3

Basically, I don't understand why the code below will output 434 when 4.35 * 100 = 435.0 which is converted to the int of 435, right?

What is the simple explanation for this and is this an issue which crops up often?

How would you get around this?

public class RoundOffDemo {
    public static void main(String[] args) {
        double price = 4.35;
        int cents = (int) (100 * price); // Should be 435
        System.out.println(cents); // Prints 434!
    }
}
1

6 Answers 6

7

The problem is that 4.35 cannot be exactly represented in binary. So 4.35 * 100 is not exactly 435.0. (Every fraction in binary is the sum of inverse powers of 2, all of which are terminating decimals. Unfortunately, 0.35 decimal has a non-terminating binary expansion. So 4.35 is more like 4.349999...x (where x is something beyond which everything is zero, courtesy of the machine's finite representation of floating point numbers.) Integer truncation then produces 434. You can use Math.round() to avoid this problem.

Sign up to request clarification or add additional context in comments.

4 Comments

Little addition, I suppose that Math.rint() is more correct to use in such case (casting to int).
@4ndrew - Perhaps so. One could also cast the argument to a float so round returns an int. (Although, for the numbers OP is using, using float to start with instead of double might be the best option.)
A good example from the "real world" is 1/3, which cannot be exactly represented in decimal. If computers used decimal instead of binary, 1/3 * 3 would be 0.9999999, not 1.0.
@Ted Hopp thanks. This question comes up so often that it's useful to mention a real world example to help make it "click".
2

IMO, the suggestions to use a BigDecimal are overkill. For cents, use a long. Even the US National Debt can be covered using a long for cents.

If you do use floats (or doubles), when converting to integers, use Math.round(), Math.floor() or Math.ceil() as appropriate.

Comments

0

if you use this code:

System.out.println(price*100); // Prints 434!

you notice its output is

434.99999999999994

which rounded is 434

4 Comments

But why does it give 434.99999999999994 in the first place? The numbers I've used shouldn't give that answer.
@m92 java is not a mathematician, as others said java has some problems with floating point precision
@AdelBoutros It is not a java problem, it is because of the double type binary representation.
@m92 Read wikipedia page about accuracy problems: en.wikipedia.org/wiki/Floating_point#Accuracy_problems
0

Double value 4.35 is actually represented as 4.349999999999999. 100 * 4.349999999999999 = 434.9999999999999. And casting it to int value will give you 434.

1 Comment

4.3499999999999996447286321199499070644378662109375, to be exact.
0

This is a result of floating point arithmetic. The (int) casting is not a rounding function, but rather a truncation as @Ted points out.

Since you are dealing with money ( as evident from your variable names ) use BigDecimal instead.

For example --

double d = 4.35;
BigDecimal bd = BigDecimal.valueOf(d);
bd  = bd.multiply(new BigDecimal(100));

3 Comments

That doesn't fix the problem; d is already not 4.35 exactly, so bd just carries this rounding error forward.
@Ted -- I actually tried it and made sure it worked before posting the code. The BigDecimal.valueOf(d) uses Double.toString(d) and consistently produces the same output. Am I making an assumption I shouldn't here?
There must be something else (probably rounding) going on somewhere in this. Decimal 4.35 is binary 100.0(1011). It just can't be exactly represented in floating point.
-1
  1. Do not use double/float for floating-point arithmetic in Java, use BigDecimal instead. This is because Java cannot represent floating-point precisely.

  2. Always use BigDecimal for temporary variables, which will be processed/involved in future calculations. Convert the values to float/double only if you want to persist them into the database.

2 Comments

I wouldn't use BigDecimal as a panacea. It doesn't really solve the rounding problem in general. For instance, if you divide and the result cannot be expressed exactly in decimal, then either you need to specify a rounding mode or you'll get an exception. But by specifying a rounding mode, you're back to the problem: even with BigDecimal, 3 * (4 / 3) != 4.
Imprecise representation of floating point numbers is not a problem inherit to just Java. But rather a problem inherent to computers running on binary architectures.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.