Gustavo
Gustavo

Reputation: 1418

Can I retain precision in Java if I make no operations to a double?

I want to store values from 0.00 to 10.00 in doubles. I only need 2 digit precision. If I make no operations to the doubles, can I guarantee I will always get what I stored in the variables?

I was looking at this answer, and tested this code:

    double a = 1.2;
    double b = 1.1;
    System.out.println(a - b);
    System.out.println(a);
    System.out.println(b);

The first print outputs an imprecise value, and the explanation, as I understood it, is how imprecisely the machine has to represent the numbers. But, why them the other two prints output precise values? Just because I made no operations? Can I assume it will always be true?

Upvotes: 0

Views: 281

Answers (3)

Eric Postpischil
Eric Postpischil

Reputation: 223484

Java uses the IEEE-754 binary32 format for its double type. This type has the property that, for numbers within its exponent range, if any decimal numeral of 15 or fewer significant digits is converted to double (using round-to-nearest, ties-to-even), and the resulting double is converted back to a 15-digit decimal numeral, the result equals the original number.

For default formatting of floating-point numbers, Java produces just enough digits to uniquely distinguish the number within the floating-point format.

A consequence of these two properties is that converting a decimal numeral with 15 or fewer significant digits to double and then converting that back to decimal using the default formatting will produce something equal to the original number. It may differ in some respects. For example, the original numeral may have trailing zeros that the produced numeral does not, such as “3.00” to “3” or “123.4500” to “123.45”.

Upvotes: 0

Nathan Hughes
Nathan Hughes

Reputation: 96424

Here are some different attempts to print out the contents of a double value:

import java.math.*;

public class FloatShow {

    public static void main(String ... args) {

        System.out.println(1.2);

        System.out.println(new Double(1.2).toString());

        System.out.println(new BigDecimal(1.2).toString());
    }
}

This prints out

1.2
1.2
1.1999999999999999555910790149937383830547332763671875

The println method given a double boxes it and calls toString, similarly the Double uses toString, which is doing some rounding for you. The BigDecimal uses the raw value to construct an instance, and presents it without cleanup. So your assumptions about precision were invalid.

Floating point and fixed decimal are two different concepts. Here is the definition of floating point arithmetic in wikipedia:

In computing, floating-point arithmetic (FP) is arithmetic using formulaic representation of real numbers as an approximation to support a trade-off between range and precision. For this reason, floating-point computation is often found in systems which include very small and very large real numbers, which require fast processing times.

But fixed decimal (like BigDecimal) is different, it stores the individual digits. BigDecimal is slower to use, but it doesn't make the kind of range-precision trade-off that floating point does.

Floating point is good for graphics and engineering applications where the calculation is the bottleneck, the inputs are not entirely precise (but precise within a known error range) and it can be calculated that the speed is worth the trade-off. For a lot of business applications the performance difference is not an issue because something else is always the bottleneck, values are known exactly, and accuracy is not negotiable.

Upvotes: 1

Billy Brown
Billy Brown

Reputation: 2342

The imprecision comes from representing the number as a double (a floating point number).

Although it does use arithmetic on floating point values, as I said above, it is the storage of the values that is the problem, although printing methods often try to hide that from us. This answer should contain enough detail: https://stackoverflow.com/a/588014/1406083

If you want to store (and operate on) decimal numbers to a precision of two decimal points, and no more, then you should not be using doubles (or floats). You should instead look to working with precise numbers (such as ints or longs). For two decimal points, you could just use 100 == 1.0, or use the BigDecimal class.

Upvotes: 1

Related Questions