Stephen Ostermiller
Stephen Ostermiller

Reputation: 25494

In what range does incrementing and decrementing Java doubles work?

I'm writing a Java class that tracks a fractional number, but needs to be able to increment and decrement it. Using a double for this number seems to work fine for small values, but I'm trying to figure out its limits.

I was thinking that one way of determining it might be to run a program that incremented a double and a long until they were no longer equal. However this takes too long to run.

double x = 0;
long y = 0;
while ((long)x == y)
{
    x++;
    y++;
}
System.out.println("X: " + x);
System.out.println("Y: " + y);

What are double's limits where you can no longer increment a value and actually add one to it? I'd think that decrementing a double would have a similar problem with negative numbers at some point.

Upvotes: 10

Views: 879

Answers (2)

plugwash
plugwash

Reputation: 10484

IEEE doubles have 52 bits in the mantissa field, but IEEE format use an "implicit 1", so this gives us an effective 53 bit mantissa. As a result all integers up to 253-1 can be represented.

253 can also be represented exactly, since it is a power of 2. So 253-1 can be safely incremented by 1.

253+1 on the other hand cannot be represented exactly, it would require an effective 54 bit mantissa.

AFAIK Java uses "round to nearest with ties rounded to even". So attempting to add 1 to 253 will give a result of 253.

Upvotes: 1

Sweeper
Sweeper

Reputation: 270770

You essentially want the largest positive number with the ulp of 1. This is the largest positive number where adding 1 will still change it.

From the Wikipedia page we can see that ulp for a normal number x between 2^e and 2^(e+1) is calculated as 2^(e-p+1), where p is the precision (number of bits in the mantissa).

enter image description here

For double, p is 53, so we want to find a value of e such that 2^(e-52) is 1. Obviously, e should be 52.

Then we can construct a double with the exponent 52, and the largest mantissa possible. You can write it as a long:

Double.longBitsToDouble(
        0x433FFFFFFFFFFFFFL
);

Due to how doubles are represented, we need to write "433" (1075 in decimal). This is the number from which subtracting 1023 will give you the desired exponent of 52.

This is the largest number where adding 1 will still change the number. nextUp of this number will remain unchanged when you add 1:

double x = Double.longBitsToDouble(
        0x433FFFFFFFFFFFFFL
);
double y = Math.nextUp(x);
System.out.println(x + 1 == x); // false
System.out.println(y + 1 == y); // true

Another way of writing x is 0x1p53 - 1 (credits to Holger in the comments), i.e. one less than the number with an exponent of 53.


The situation is symmetric for decrementing:

double x = -Double.longBitsToDouble(
        0x433FFFFFFFFFFFFFL
);
double y = Math.nextDown(x);
System.out.println(x - 1 == x); // false
System.out.println(y - 1 == y); // true

Upvotes: 14

Related Questions