Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Comparing "0.1" to .1

I am working on understanding the limitations of double. 0.1 cannot be expressed in a finite system because it is a repeating binary value. But, when I declare a variable and give it 0.1, it still prints as 0.1 and not .0999999999999999, 1.000000000000001 or something of the like. But, if I add a variable that has 0.1 ten times, it comes out to .9999999999999999, which is what I would expect.

The real question is why is it printing exactly at 0.1 when I know that is impossible and I have proof that is not the true value?

double d  = 0.1;
System.out.printf("%.16f\n",d);
System.out.printf("%.16f", d + d + d + d + d + d + d + d + d + d);

What I am trying to do is compare a string literal to a double conversion to see if the value in the string can be exactly represented in double. For example:

".5" == .5? Expected answer: Yes.

".1" == .1? Expected answer: No.

The comparison I am trying is

number = new BigDecimal("0.1");
d = number.doubleValue();
if (number.compareTo(new BigDecimal(Double.toString(d))) == 0){
    // Doing something
}

Any help understanding why this is not distinguishing between values that can and cannot be represented by double would be appreciated.

like image 879
Tom K Avatar asked Sep 28 '22 10:09

Tom K


1 Answers

To test if a String represents a double value you can do this:

private static boolean isADouble(String s) {
    return new BigDecimal(s).equals(new BigDecimal(Double.parseDouble(s)));
}

public static void main(String[] args) {
    System.out.println(isADouble("0.1"));  // false
    System.out.println(isADouble("0.5"));  // true
}

This works because new BigDecimal(s) produces a BigDecimal exactly equal to the value represented by the String, whereas new BigDecimal(Double.parseDouble(s)) is the exact value of the closest double to that value.

I will explain at the end why number.compareTo(new BigDecimal(Double.toString(d))) == 0 does not work.

When you do

double d = 0.1;
System.out.println(d);

you get the answer 0.1 because d is the closest double to the "real" 0.1. (double d = 0.1; means "make d the nearest double to 0.1". When you write

System.out.println(d + d + d + d + d + d + d + d + d + d);

you see 0.9999999999999999 rather than 1. The reason you don't see 1 is because there are double values closer to one than the answer (in fact one is a double value itself). There is no reason why the answer should be the closest double to one, because d wasn't really 0.1 in the first place (it was just close to it), and in any case when you add floating point numbers inaccuracies are introduced.

Finally, number.compareTo(new BigDecimal(Double.toString(d))) == 0 does not work because even though number.doubleValue() is not exactly 0.1, Double.toString() still converts it to the String "0.1" because there are no double values closer the "real" 0.1.

like image 89
Paul Boddington Avatar answered Oct 06 '22 01:10

Paul Boddington