Java - maximum loss of precision in one double add

2019-04-07 09:05发布

问题:

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.

回答1:

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.



回答2:

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.



回答3:

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