8

My app is producing doubles where Double.toString() produces "-3.1999999999999953" - whereas I would expect it to produce "-3.2".

I'm actually getting these doubles from JScience's Amount#getEstimatedValue().

I don't want to set an arbitrary number of digits for precision, since I don't know how many digits will be significant, but I don't want it to produce numbers that end in "99999999.*".

How can I convert Doubles to Strings without this problem?

trashgod
  • 203,806
  • 29
  • 246
  • 1,045
sanity
  • 35,347
  • 40
  • 135
  • 226
  • 3
    Don't use values that don't have a finite binary expansion. – Kerrek SB Dec 15 '11 at 17:21
  • Do you actually need *floating* point numbers? Maybe fixed-point arithmetic will suffice for your needs? – Kerrek SB Dec 15 '11 at 17:23
  • 1
    That's not very helpful, I don't control what numbers I must deal with. – sanity Dec 15 '11 at 17:23
  • Use [DecimalFormat](http://docs.oracle.com/javase/6/docs/api/java/text/DecimalFormat.html) – Sean Patrick Floyd Dec 15 '11 at 17:24
  • And yes, I need to be able to deal with floating point numbers. – sanity Dec 15 '11 at 17:24
  • The problem is fairly ill defined, so you should definitely put more mental effort into nailing down the requirements. I'd say you could do something like format the string with an increasing level of precision, up to 16, until the final digits are all zero; then you go back to the precision which produces *no* trailing zeros. Very expensive, but such is the nature of your question. *Edit:* Or better, *start* at 16 and reduce as long as all trailing digits are zero. – Kerrek SB Dec 15 '11 at 17:27
  • 4
    If you don't know what precision you need, how can you know that `-3.1999999999999953` is not the exact value? – Toomai Dec 15 '11 at 17:28
  • possible duplicate of [How to round the double value to 2 decimal points?](http://stackoverflow.com/questions/5945867/how-to-round-the-double-value-to-2-decimal-points) – John B Dec 15 '11 at 17:43
  • you can't have your cake *and* eat it – David Heffernan Dec 15 '11 at 18:15
  • Possible duplicate of [Is there a way to make JScience output in a more "human friendly" format?](http://stackoverflow.com/questions/8514293/is-there-a-way-to-make-jscience-output-in-a-more-human-friendly-format) [`Amount`](http://jscience.org/api/org/jscience/physics/amount/Amount.html) already has the needed error bounds. – trashgod Dec 21 '11 at 14:29

3 Answers3

7

Recommended solution

BigDecimal.valueOf (hisDouble).toPlainString ()

The hack provided later in the last section of this post was the first thing that came to mind when trying to solve OPs question.

Then a friend asked what I was doing and said that OP be better of using BigDecimal and I went into facepalm mode..

But I'll leave the hack in this post so that the world can see how stupid I can be sometimes.


When printing you can use System.out.format.

The snippet below will will round the value of yourDecimal to one decimal and then print the value.

Double yourDouble = -3.1999999999999953;
System.out.format ("%.1f", yourDouble);

output

-3.2

The most stupid hack ever written

  public static String fixDecimal (Double d) {
    String  str = "" + d;
    int    nDot = str.indexOf ('.');

    if (nDot == -1)
      return str;

    for (int i = nDot, j=0, last ='?'; i < str.length (); ++i) {
      j = str.charAt (i) == last ? j+1 : 0;

      if (j > 3)
        return String.format ("%."+(i-nDot-j-1)+"f", d);

      last = str.charAt (i);
    }

    return str;
  }

...

Double[] testcases = {
  3.19999999999953,
  3.145963219488888,
  10.4511111112,
  100000.0
};

for (int i =0; i < testcases.length; ++i)
  System.out.println (
    fixDecimal (testcases[i]) + "\n"
  );

output

3.2
3.1459632195
10.45
100000.0
Filip Roséen - refp
  • 62,493
  • 20
  • 150
  • 196
4

Use BigDecimal:

System.out.println(BigDecimal.valueOf(-3.2d).toPlainString());

Output:

-3.2
Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
3

You could try

http://docs.oracle.com/javase/1.4.2/docs/api/java/text/DecimalFormat.html

Slightly "heavyweight", but should do the trick. Some example usage at:

DecimalFormat subpattern boundary not working right

Community
  • 1
  • 1
Brian
  • 6,391
  • 3
  • 33
  • 49