0

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?

Gustavo
  • 1,332
  • 2
  • 16
  • 39
  • If you just want 2-dp precision from 0 to 10, then `double` (or `float`) will always have enough precision. – Sweeper May 27 '20 at 13:13
  • 1.1 and 1.2 cannot be stored exactly in binary floating point, but the values stored are close enough that you don't necessarily notice the difference. – khelwood May 27 '20 at 13:14
  • @khelwood Can you give me an example where `double a = xyz; System.out.println(a);` will nor produce xyz? – Gustavo May 27 '20 at 13:16
  • @NathanHughes What could I use, instead? – Gustavo May 27 '20 at 13:23
  • @Sweeper The link I posted says that 1.2 is in fact 1.1999999999999999555910790149937383830547332763671875. The comments below say it's only a matter of looking like precise, when in fact it's imprecise. – Gustavo May 27 '20 at 13:25
  • Try: `double a = 1.0000000000000001` – khelwood May 27 '20 at 13:32
  • "1.2 is in fact 1.1999999999999999555910790149937383830547332763671875" That's enough for you, isn't it? You only want it precise to 2 decimal places. `double` is precise to 17 significant figures. Unless you want to represent, say, `1.19999999999999991111`, you can use `double`. – Sweeper May 27 '20 at 13:32

3 Answers3

1

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.

Billy Brown
  • 2,272
  • 23
  • 25
  • `int` or `long` values are not “precise numbers” and cannot store numbers “to a precision of two decimal points”. Integer types are manifestly less precise than floating-point types, at least over much of their domains. – Eric Postpischil May 27 '20 at 16:29
  • @EricPostpischil 'precise' was not the word that I wanted to use, but I couldn't think of any other at the time (and the question asker mentioned 'precise'). I think that 'exact' is what I was looking for. – Billy Brown May 28 '20 at 07:34
1

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.

Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
  • Business calculations are also special because the input values are often known exactly. For most science and engineering calculations the inputs include measured quantities. Measurement error typically dwarfs the rounding error on converting to double. – Patricia Shanahan May 27 '20 at 14:08
  • @Patricia: that's true, i'll try to add that. – Nathan Hughes May 27 '20 at 14:14
  • @NathanHughes So the answer to the original question is NO, in the sense that there is no precision at any time, even if I make no calculation, but YES in the sense that 2 digit precision plus no calculations will never get me to boundaries where the rounding of printing methods won't be enough. – Gustavo May 27 '20 at 14:23
  • errrrr. never? errors can accumulate. yes you can round every step of the way to avoid that. but that's as big a pain as using BigDecimal. – Nathan Hughes May 27 '20 at 17:56
0

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”.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312