The Java double
type (almost) conforms to an international standard called IEEE-754, for floating point numbers. The numbers that can be expressed in this type all have one thing in common - their representations in binary terminate after at most 53 significant digits.
Most numbers with terminating decimal representations do not have terminating binary representations, which means there's no double
that stores them exactly. When you write a double
literal in Java, the value stored in the double
will generally not be the number you wrote in the literal - instead it will be the nearest available double
.
The literal 1.23456789
is no exception. It falls neatly between two numbers that can be stored as double
, and the exact values of those two double
numbers are 1.2345678899999998900938180668163113296031951904296875 and 1.23456789000000011213842299184761941432952880859375. The rule is that the closer of those two numbers is chosen, so the literal 1.23456789
is stored as 1.2345678899999998900938180668163113296031951904296875.
The literal 1E8
can be stored exactly as a double
, so the multiplication in your first example is 1.2345678899999998900938180668163113296031951904296875 times 100000000, which of course is 123456788.99999998900938180668163113296031951904296875. This number can't be stored exactly as a double
. The nearest double
below it is 123456788.99999998509883880615234375 and the nearest double
above it is 123456789 exactly. However, the double
below it is closer, so the value of the Java expression 1.23456789 * 1E8
is actually 123456788.99999998509883880615234375. When you apply the Math.floor
method to this number, the result is exactly 123456788.
The literal 1E9
can be stored exactly as a double
, so the multiplication in your second example is 1.2345678899999998900938180668163113296031951904296875 times 1000000000, which of course is 1234567889.9999998900938180668163113296031951904296875. This number can't be stored exactly as a double
. The nearest double
below it is 1234567889.9999997615814208984375 and the nearest double above it is 1234567890 exactly. But this time, the double
above it is closer, so the value of the Java expression 1.23456789 * 1E9
is exactly 1234567890, which is unchanged by the Math.floor
method.
The second part of your question was how to avoid this. Well, if you want to do exact calculations involving numbers with terminating decimal representations, you must not store them in double
variables. Instead, you can use the BigDecimal
class, which lets you do things like this
BigDecimal a = new BigDecimal("1.23456789");
BigDecimal b = new BigDecimal("100000000");
BigDecimal product = a.multiply(b);
and the numbers are represented exactly.