<< 1.03 - .42 = ? | Home | Subversion Comes To Cygwin >>

1.03 - .42 = 0.6100000000000001? No Way!

For some reason, I can't seem to let this one go. I've read the comments on this blog (thanks Dan for all the information). I've talked with a few other people.

I have verified that Java and C++ represents the numbers 1.03, 0.42, and the result of 1.03 - 0.42 the same way by using

  • java.lang.Double.doubleToLongBits(double)
  • java.lang.Long.toHexString(long)

in Java and

  • double d, *p = &d;
  • long long l = *(reinterpret_cast<long long*>(p))

in C++.

So the differences are in the way doubles are printed.

Jeff Brown mentioned to me at the St. Louis JUG meeting Thursday that there might be other floating point numbers that Java prints right and other languages print wrong.

But a little experiment (a very superficial one, I admit) indicates that Java is wrong more times than C++:

[weiqi@gao] $ cat Foo.java
public class Foo {
  public static void main(String[] args) {
    double ds[] = {0.1, 0.2, 0.3, 0.4};
    for (int i = 0; i < 4; i++) {
      for (int j = i + 1; j < 4; j++) {
        System.out.println("" + ds[j] + "-" + ds[i] +
          "=" + (ds[j] - ds[i]));
      }
    }
  }
}
[weiqi@gao] $ javac Foo.java
[weiqi@gao] $ java Foo
0.2-0.1=0.1
0.3-0.1=0.19999999999999998
0.4-0.1=0.30000000000000004
0.3-0.2=0.09999999999999998
0.4-0.2=0.2
0.4-0.3=0.10000000000000003
[weiqi@gao] $ cat foo.cc
#include <iostream>
int main() {
  double ds[] = {0.1, 0.2, 0.3, 0.4};
  for (int i = 0; i < 4; i++) {
    for (int j = i + 1; j < 4; j++) {
      std::cout << ds[j] << "-" << ds[i] << "="
        << (ds[j] - ds[i]) << std::endl;
    }
  }
}
[weiqi@gao] $ make foo
g++     foo.cc   -o foo
[weiqi@gao] $ ./foo
0.2-0.1=0.1
0.3-0.1=0.2
0.4-0.1=0.3
0.3-0.2=0.1
0.4-0.2=0.2
0.4-0.3=0.1

Then Jeff Grigg interjected with the sarcastic "Java is right and everything else is wrong."

Somehow, I don't feel so confident.

The Javadoc for java.lang.Double.toString(double) details how Java prints a double. It specifically mentioned this:

How many digits must be printed for the fractional part of m or a? There must be at least one digit to represent the fractional part, and beyond that as many, but only as many, more digits as are needed to uniquely distinguish the argument value from adjacent values of type double.

This might be the root of the problem. What if two adjacent doubles have decimal representations that differ only in insignificant digits? By the above rule, insignificant digits will be printed just so that the two numbers can be distinguished.

Does such pairs of adjacent doubles exist? I don't know. My suspicion is that they do exist, and 1.03 - .42 belongs to such a pair. Of course, it could also be something more complicated that I don't understand.



Re: 1.03 - .42 = 0.6100000000000001? No Way!

No, it's nothing too complicated. As always, floating point numbers are just approximations. If you want arbitrary precision use BigDecimal http://java.sun.com/j2se/1.4.2/docs/api/java/math/BigDecimal.html

Re: 1.03 - .42 = 0.6100000000000001? No Way!

You're right, Michael. The whole point of Item 31 is to get people to use BigDecimal for monetary calculations.

Re: 1.03 - .42 = 0.6100000000000001? No Way!

No he's not. He's condescending and misses the point. <p/> No, it's nothing too complicated. As always, floating point numbers are just approximations. If you want arbitrary precision use BigDecimal http://java.sun.com/j2se/1.4.2/docs/api/java/math/BigDecimal.html <p/> Even if you use BigDecimal with a fine precision, it's still just a computer approximation of a real number. <p/> The question you didn't address is how many digits to display. What Weiqi correctly observes is that the default behavior for Java is not what is desireable from a point of significance. <p/> I would be interested in a Java component that would track significant digits through each math operation, and can display the correct results.

Re: 1.03 - .42 = 0.6100000000000001? No Way!

Secion 4.2.4 of The Java Language Specification (Floating-Point Operations) talks a little about this. If you divide 1.0 by a number and mutliply that result by the same number, the result should be 1.0. Try this...
        // if we divide 1.0 by a number (x) and multiply that
        // result by x, the result _should_ be 1.0.
        // the following finds some cases that don't work

        // checking double values...
        for (int i = 0; i < 100; i++) {
            boolean precisionProblem = ((1.0 / i) * i) != 1.0;
            if (precisionProblem) {
                System.out.println("double " + i + " had a problem.");
            }
        }

        // do the same with floating point numbers...
        for (int i = 0; i < 100; i++) {
            boolean precisionProblem = ((1.0f / i) * i) != 1.0f;
            if (precisionProblem) {
                System.out.println("float " + i + " had a problem.");
            }
        }
The chapter in the language spec provides a different piece of sample code that exposes the same things (and some other things).

Re: 1.03 - .42 = 0.6100000000000001? No Way!

Well, none of the documentations and web pages pin down the exact number of significant digits of a double.

For the number at hand (the double that is the result of doing a 1.032 - .42), its exact value is

A =0.6100000000000000976996261670137755572795867919921875

The smaller adjacent double is

A_s = 0.60999999999999998667732370449812151491641998291015625

The greater adjacent double is

A_g = 0.61000000000000020872192862952942959964275360107421875

Thus our double represents all real numbers between

0.610000000000000042188474935755948536098003387451171875 (A - (A - A_s) / 2)

and

0.610000000000000153210777398271602578461170196533203125 (A + (A_g - A) / 2)

The number printed by Java

0.6100000000000001

interpreted in the normal mathematical sense, represents all real numbers between

0.61000000000000005

and

0.61000000000000015

This latter interval is strictly smaller then the actual interval. In other words, the last digit "1" is insignificant.

There. I'm done splitting hairs!

Re: 1.03 - .42 = 0.6100000000000001? No Way!

1.03 - .42, not 1.032 - .42, of course.

Re: 1.03 - .42 = 0.6100000000000001? No Way!

<h1>Please check some relevant pages in the field of- Tons of interesdting stuff!!! </h1>

Add a comment Send a TrackBack