2

I was just messing around with this method to see what it does. I created a variable with value 3.14 just because it came to my mind at that instance.

double n = 3.14;

System.out.println(Math.nextUp(n));

The preceding displayed 3.1400000000000006.

Tried with 3.1400000000000001, displayed the same.

Tried with 333.33, displayed 333.33000000000004.

With many other values, it displays the appropriate value for example 73.6 results with 73.60000000000001.

What happens to the values in between 3.1400000000000000 and 3.1400000000000006? Why does it skips some values? I know about the hardware related problems but sometimes it works right. Also even though it is known that precise operations cannot be done, why is such method included in the library? It looks pretty useless due to the fact that it doesn't work always right.

Haggra
  • 3,539
  • 2
  • 20
  • 28
  • 1
    Turns out that floating point numbers are not smooth (continuous), they have gaps as a consequence of limited precision. I think your "missing values" reflect those gaps, which are pretty small in the range you're dealing with. To explain the behavior you are seeing we'd need to get down to the binary-level of floating point representation. See here for more: http://stackoverflow.com/questions/872544/precision-of-floating-point – jgreve May 11 '16 at 14:12
  • As a follow up I'm reading this (from the earlier link): "What Every Computer Scientist Should Know About Floating-Point Arithmetic" http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html just because it looks interesting - it is a reprint of an ACM article. – jgreve May 11 '16 at 14:36
  • You can look at http://stackoverflow.com/questions/2100490/floating-point-inaccuracy-examples – Alexandre Cartapanis Jun 03 '16 at 09:49

3 Answers3

2

One useful trick in Java is to use the exactness of new BigDecimal(double) and of BigDecimal's toString to show the exact value of a double:

import java.math.BigDecimal;

public class Test {
  public static void main(String[] args) {
    System.out.println(new BigDecimal(3.14));
    System.out.println(new BigDecimal(3.1400000000000001));
    System.out.println(new BigDecimal(3.1400000000000006));
  }
}

Output:

3.140000000000000124344978758017532527446746826171875
3.140000000000000124344978758017532527446746826171875
3.1400000000000005684341886080801486968994140625

There are a finite number of doubles, so only a specific subset of the real numbers are the exact value of a double. When you create a double literal, the decimal number you type is represented by the nearest of those values. When you output a double, by default, it is shown as the shortest decimal fraction that would round to it on input. You need to do something like the BigDecimal technique I used in the program to see the exact value.

In this case, both 3.14 and 3.1400000000000001 are closer to 3.140000000000000124344978758017532527446746826171875 than to any other double. The next exactly representable number above that is 3.1400000000000005684341886080801486968994140625

Patricia Shanahan
  • 25,849
  • 4
  • 38
  • 75
  • Why would such a method included in API? Is there any usefulness in it perhaps? – Haggra May 11 '16 at 14:59
  • 1
    @Haggra Personally, I use nextUp etc. most often when I'm probing floating point behavior, the sort of thing you were doing in your question. Typically, the exact result of a calculation is bracketed by a pair of consecutive exactly representative numbers, and I want to see what they are, and where the result falls relative to the half way point between them. – Patricia Shanahan May 11 '16 at 15:11
1

Like @jgreve mentions this has to do due to the use of float & double primitives types in java, which leads to the so called rounding error. The primitive type int on the other hand is a fixed-point number meaning that it is able to "fit" within 32-bits. Doubles are not fixed-point, meaning that the result of double calculations must often be rounded in order to fit back into its finite representation, which leads sometimes (as presented in your case) to inconsistent values.

See the following two links for more info.

https://stackoverflow.com/a/322875/6012392

https://en.wikipedia.org/wiki/Double-precision_floating-point_format

A work around could be the following two, which gives a "direction" to the first double.

double n = 1.4;
double x = 1.5;
System.out.println(Math.nextAfter(n, x)); 

or

double n = 1.4;
double next = n + Math.ulp(n);
System.out.println(next);

But to handle floating point values it is recommended to use the BigDecimal class

Community
  • 1
  • 1
M. Suurland
  • 725
  • 12
  • 31
1

Floating point numbers are stored in binary: the decimal representation is just for human consumption.

Using Rick Regan's decimal to floating point converter 3.14 converts to:

11.001000111101011100001010001111010111000010100011111

and 3.1400000000000006 converts to

11.0010001111010111000010100011110101110000101001

which is indeed the next binary number to 53 significant bits.

Simon Byrne
  • 7,694
  • 1
  • 26
  • 50