18

I have a double value d and would like a way to nudge it very slightly larger (or smaller) to get a new value that will be as close as possible to the original but still strictly greater than (or less than) the original.

It doesn't have to be close down to the last bit—it's more important that whatever change I make is guaranteed to produce a different value and not round back to the original.

(This question has been asked and answered for C, C++)

The reason I need this, is that I'm mapping from Double to (something), and I may have multiple items with the save double 'value', but they all need to go individually into the map.

My current code (which does the job) looks like this:


private void putUniqueScoreIntoMap(TreeMap map, Double score,
            A entry) {

        int exponent = 15;
        while (map.containsKey(score)) {
            Double newScore = score;
            while (newScore.equals(score) && exponent != 0) {
                newScore = score + (1.0d / (10 * exponent));
                exponent--;
            }
            if (exponent == 0) {
                throw new IllegalArgumentException("Failed to find unique new double value");
            }
            score = newScore;
        }
        map.put(score, entry);
    }

Community
  • 1
  • 1
barryred
  • 1,103
  • 1
  • 17
  • 27

6 Answers6

29

In Java 1.6 and later, the Math.nextAfter(double, double) method is the cleanest way to get the next double value after a given double value.

The second parameter is the direction that you want. Alternatively you can use Math.nextUp(double) (Java 1.6 and later) to get the next larger number and since Java 1.8 you can also use Math.nextDown(double) to get the next smaller number. These two methods are equivalent to using nextAfter with Positive or Negative infinity as the direction double.

Specifically, Math.nextAfter(score, Double.MAX_VALUE) will give you the answer in this case.

Aequitas
  • 2,205
  • 1
  • 25
  • 51
Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
20

Use Double.doubleToRawLongBits and Double.longBitsToDouble:

double d = // your existing value;
long bits = Double.doubleToLongBits(d);
bits++;
d = Double.longBitsToDouble(bits);

The way IEEE-754 works, that will give you exactly the next viable double, i.e. the smallest amount greater than the existing value.

(Eventually it'll hit NaN and probably stay there, but it should work for sensible values.)

Kelly S. French
  • 12,198
  • 10
  • 63
  • 93
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    Is that going to be reliable on both endian-nesses? – Donal Fellows Sep 07 '10 at 11:38
  • @Jon Skeet - Why is this a better answer than using Double.MIN_VALUE? – Erick Robertson Sep 07 '10 at 11:54
  • 3
    @Erick: Because that wouldn't work :) See my comments on Richard's answer. – Jon Skeet Sep 07 '10 at 12:11
  • Jon, and what will happen if mantis is all 1-s ? is it fine if carry will go to exponent? exponent will be incremented, so 1.11111...1eX will become 10.0000eX => 1.00000eX+1 . i have a feeling that it can break normalization at some point. is not it safer to leave FP operations to FPU? – Andrey Sep 07 '10 at 13:02
  • @Andrey: At some point it will become infinity or NaN, but the bits in IEEE-754 have been ordered so that it will work within actual numbers (including denormals). When the mantissa is "full", the exponent will increase and you'll still just get the "next highest" value. – Jon Skeet Sep 07 '10 at 13:03
  • @Jon Skeet - there is a cleaner way in Java 1.6 and above - see my answer – Stephen C Sep 07 '10 at 13:49
  • How about instead we solve the OPs actual problem of storing several values for a key in a map? – Christoffer Hammarström Sep 07 '10 at 14:00
3

Have you considered using a data structure which would allow multiple values stored under the same key (e.g. a binary tree) instead of trying to hack the key value?

John
  • 110
  • 1
-2

d += Double.MIN_VALUE

(or -= if you want to take away)

nobody
  • 19,814
  • 17
  • 56
  • 77
Amy B
  • 17,874
  • 12
  • 64
  • 83
  • 5
    Given how small `MIN_VALUE` is, this will often result in `d` not changing. – AakashM Sep 07 '10 at 11:26
  • 4
    not correct. if number is big then after addition and normalization `Double.MIN_VALUE` will evaporate and numer will not change – Andrey Sep 07 '10 at 11:29
-2

What about using Double.MIN_VALUE?

Fernando Miguélez
  • 11,196
  • 6
  • 36
  • 54
-4

Use Double.MIN_VALUE.

The javadoc for it:

A constant holding the smallest positive nonzero value of type double, 2-1074. It is equal to the hexadecimal floating-point literal 0x0.0000000000001P-1022 and also equal to Double.longBitsToDouble(0x1L).

Richard J. Ross III
  • 55,009
  • 24
  • 135
  • 201
  • 1
    And what happens when you add that to a reasonably large value? – Jon Skeet Sep 07 '10 at 11:37
  • It might overflow, or it might make no difference at all, but, it is a legitimate answer to his question, as it is the smallest possible value for a double that is positive. – Richard J. Ross III Sep 07 '10 at 11:55
  • 3
    @Richard: Making no difference at all means it *isn't* a legitimate answer to the question: "It doesn't have to be close down to the last bit—it's more important that whatever change I make is guaranteed to produce a different value and not round back to the original." You've got to create a different value. Adding `Double.MIN_VALUE` won't always do that. – Jon Skeet Sep 07 '10 at 12:11
  • @Jon Skeet: I'm not quite understanding how the two answers are different. The JavaDocs say that Double.MIN_VALUE is the same as Double.logBitsToDouble(0x1L) which seems the same as "long bits = Double.doubleToLongBits(d); bits++;". Is there any more you can clarify? " – Kelly S. French Sep 07 '10 at 13:54
  • 3
    @Kelly: There's a difference between adding `Double.MIN_VALUE` to a double value, and incrementing the bit pattern representing a double. They're entirely different operations, due to the way that floating point numbers are stored. If you try to add a very little number to a very big number, the difference may well be so small that the closest result is the same as the original. Adding 1 to the current bit pattern, however, will always change the corresponding floating point value, by the smallest possible value *which is visible at that scale*. – Jon Skeet Sep 07 '10 at 14:10
  • @Jon: I moved my question here: http://stackoverflow.com/questions/3660525/are-these-two-approaches-to-the-smallest-double-values-in-java-equivalent – Kelly S. French Sep 07 '10 at 16:20