7

Is it possible to establish, even roughly, what the maximum precision loss would be when dealing with two double values in java (adding/subtracting)? Probably the worst case scenario is when two numbers cannot be represented exactly, and then an operation is performed on them, which results in a value that also cannot be represented exactly.

Bober02
  • 15,034
  • 31
  • 92
  • 178
  • If precision loss is an issue, I would recommend to use BigDecimal instead of double. – jeroen_de_schutter Nov 20 '12 at 10:11
  • Yeah, the whole point of this question is to see how big the loss is. double takes up 8 bytes, while BigDecimal around 40, and I need to store a lot of data points – Bober02 Nov 20 '12 at 10:12
  • When you do a lot of floating-point operations, the performance gain of using primitives instead of objects can be significant. – Cephalopod Nov 20 '12 at 10:37

3 Answers3

7

The worst case is that all precision can be lost. This can for example happen if the result is larger than the largest representable finite number. Then it will be stored as POSITIVE_INFINITY (or NEGATIVE_INFINITY).

Regarding your update, it can happen with addition.

double a = Double.MAX_VALUE;
System.out.println(a);
double b = a + a;
System.out.println(b);

Result:

1.7976931348623157E308
Infinity

See it online: ideone

In general the size of the representation error is relative to the size of your numbers.

Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
5

Have a look at Math.ulp(double). The ulp of a double is the delta to the next highest value. For instance, if you add to numbers and one is smaller than the ulp of the other, you know that the addition will have no effect. If you multiply two doubles, you can multiply their ulps to get the maximum error of the result.

Cephalopod
  • 14,632
  • 7
  • 51
  • 70
0

You could have a look at the actual precision of your inputs, for example the code below outputs:

input: 0.01000000000000000020816681711721685132943093776702880859375
range: [0.0099999999999999984734433411404097569175064563751220703125 - 0.010000000000000001942890293094023945741355419158935546875]
range size: 3.4694469519536141888238489627838134765625E-18
input: 10000000000000000
range: [9999999999999998 - 10000000000000002]
range size: 4
public static void main(String[] args) {
    printRange(0.01);
    printRange(10000000000000000d);
}

private static void printRange(double d) {
    long dBits = Double.doubleToLongBits(d);
    double dNext = Double.longBitsToDouble(dBits + 1);
    double dPrevious = Double.longBitsToDouble(dBits + -1);
    System.out.println("input: " + new BigDecimal(d));
    System.out.println("range: [" + new BigDecimal(dPrevious) + " - " + new BigDecimal(dNext) + "]");
    System.out.println("range size: " + new BigDecimal(dNext - dPrevious));
}

You would still need to then estimate the loss on the result of your operation. And that does not work with corner cases (around Infinity, NaN etc.).

assylias
  • 321,522
  • 82
  • 660
  • 783