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!
    }
}
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
mino
  • 6,978
  • 21
  • 62
  • 75
  • possible duplicate of [Floating point arithmetic not producing exact results in Java](http://stackoverflow.com/questions/1661273/floating-point-arithmetic-not-producing-exact-results-in-java). And at least a hundred other questions. – dan04 Dec 28 '11 at 23:46

6 Answers6

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.

Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
  • Little addition, I suppose that Math.rint() is more correct to use in such case (casting to int). – 4ndrew Dec 28 '11 at 17:11
  • @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.) – Ted Hopp Dec 28 '11 at 17:19
  • 2
    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. – user949300 Dec 28 '11 at 18:36
  • @Ted Hopp thanks. This question comes up so often that it's useful to mention a real world example to help make it "click". – user949300 Dec 28 '11 at 18:56
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.

user949300
  • 15,364
  • 7
  • 35
  • 66
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));
Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
Kal
  • 24,724
  • 7
  • 65
  • 65
  • 1
    That doesn't fix the problem; `d` is already not 4.35 exactly, so `bd` just carries this rounding error forward. – Ted Hopp Dec 28 '11 at 18:42
  • @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? – Kal Dec 28 '11 at 19:27
  • 1
    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. – Ted Hopp Dec 28 '11 at 19:43
0

if you use this code:

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

you notice its output is

434.99999999999994

which rounded is 434

Adel Boutros
  • 10,205
  • 7
  • 55
  • 89
  • But why does it give 434.99999999999994 in the first place? The numbers I've used shouldn't give that answer. – mino Dec 28 '11 at 16:59
  • @m92 java is not a mathematician, as others said java has some problems with floating point precision – Adel Boutros Dec 28 '11 at 17:13
  • 1
    @AdelBoutros It is not a java problem, it is because of the double type binary representation. – 4ndrew Dec 28 '11 at 17:20
  • @m92 Read wikipedia page about accuracy problems: http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems – 4ndrew Dec 28 '11 at 17:25
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.

Drona
  • 6,886
  • 1
  • 29
  • 35
-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.

Sanjay Kumar
  • 1,474
  • 14
  • 22
  • 1
    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. – Ted Hopp Dec 28 '11 at 17:00
  • 1
    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. – Perception Dec 28 '11 at 17:19