views:

7736

answers:

17
public class doublePrecision {

public static void main(String[] args) {
 double total = 0;
 total += 5.6;
 total += 5.8;
 System.out.println(total);

}

}

Which returns

11.399999999999

Okay clarifying the question a bit, how would i get this to just print (or be able to use it as) 11.4?

+2  A: 

Multiply everything by 100 and store it in a long as cents.

Paul Tomblin
Nobody mentioned money!
Draemon
@Draemon - look at the post before the last edit - all that "shoppingTotal" and "calcGST" and "calcPST" stuff looks like money to me.
Paul Tomblin
Ah, I see. Never mind then.
Draemon
+5  A: 

Pretty sure you could've made that into a three line example. :)

If you want exact precision, use BigDecimal. Otherwise, you can use ints multiplied by 10 ^ whatever precision you want.

Dustin
+1  A: 

Doubles are approximations of the decimal numbers in your Java source. You're seeing the consequence of the mismatch between the double (which is a binary-coded value) and your source (which is decimal-coded).

Java's producing the closest binary approximation. You can use the java.text.DecimalFormat to display a better-looking decimal value.

S.Lott
A: 

Use a BigDecimal. It even lets you specify rounding rules (like ROUND_HALF_EVEN, which will minimize statistical error by rounding to the even neighbor if both are the same distance; i.e. both 1.5 and 2.5 round to 2).

Adam Jaskiewicz
+7  A: 

Observe that you'd have the same problem if you used limited-precision decimal arithmetic, and wanted to deal with 1/3: 0.333333333 * 3 is 0.999999999, not 1.00000000.

Unfortunately, 5.6, 5.8 and 11.4 just aren't round numbers in binary, because they involve fifths. So the float representation of them isn't exact, just as 0.3333 isn't exactly 1/3.

If all the numbers you use are non-recurring decimals, and you want exact results, use BigDecimal. Or as others have said, if your values are like money in the sense that they're all a multiple of 0.01, or 0.001, or something, then multiply everything by a fixed power of 10 and use int or long (addition and subtraction are trivial: watch out for multiplication).

However, if you are happy with binary for the calculation, but you just want to print things out in a slightly friendlier format, try java.util.Formatter or String.format. In the format string specify a precision less than the full precision of a double. To 10 significant figures, say, 11.399999999999 is 11.4, so the result will be almost as accurate and more human-readable in cases where the binary result is very close to a value requiring only a few decimal places.

The precision to specify depends a bit on how much maths you've done with your numbers - in general the more you do, the more error will accumulate, but some algorithms accumulate it much faster than others (they're called "unstable" as opposed to "stable" with respect to rounding errors). If all you're doing is adding a few values, then I'd guess that dropping just one decimal place of precision will sort things out. Experiment.

Steve Jessop
No, do *not* use double with monetary values! You need precision with money, use BigDecimal instead. Otherwise, your answer is good. Anything you need precision with, use BigDecimal, if precision isn't that important, you can use float or double.
MetroidFan2002
The question no longer states or implies that money is involved. I specifically say to use BigDecimal or integers for money. What's the problem?
Steve Jessop
And equal to "don't use double for money" is "don't use BigDecimal *or* double for thirds". But sometimes a problem involves division, in which cases all bases not divisible by all prime factors of all denominators are about equally bad.
Steve Jessop
.9999 = 1 if your precision is less than 4 significant digits
Brian Leahy
+14  A: 
coobird
+3  A: 

As others have noted, not all decimal values can be represented as binary since decimal is based on powers of 10 and binary is based on powers of two.

If precision matters, use BigDecimal, but if you just want friendly output:

System.out.printf("%.2f\n", total);

Will give you:

11.40
Draemon
A: 

Check out BigDecimal, it handles problems dealing with floating point arithmetic like that.

The new call would look like this:

term[number].coefficient.add(co);

Use setScale() to set the number of decimal place precision to be used.

+2  A: 

Use java.math.BigDecimal

Doubles are binary fractions internally, so they sometimes cannot represent decimal fractions to the exact decimal.

Kevin Crowell
-1 for blindly recommending BigDecimal. If you don't actually *need* decimal arithmetic (i.e., if you're doing calculations with money), then BigDecimal doesn't help you. It doesn't solve all of your floating-point errors: You still have to deal with 1/3*3=0.9999999999999999999999999999 and sqrt(2)**2=1.999999999999999999999999999. Furthermore, BigDecimal carries a huge speed penalty. Worse, because of Java's lack of operator overloading, you have to rewrite all of your code.
dan04
@dan04 - If you do calculation with money why use floating representation knowing the inherent error in it.... Since there are no fraction of cents you can use decimal and calculate cents instead of using approximate dollar you have exact cent amount. If you really want the fraction of cent use a a long and calculate thousands of cents. Further more the OP made no mention of irrational numbers, all he was concerned about was addition. Do read the post carefully and understand the problem before you answer, might save you some embarrassment.
Newtopian
@Newtopian: I have nothing to be embarrassed about. The OP made *NO* mention of money, nor any indication that his problem has any inherent decimalness.
dan04
@ dan04 - No the OP did not... YOU did And Blindly offered out of context opinion to what most likely was a perfectly acceptable answer given the poor amount of details provided
Newtopian
+1  A: 

You can't, because 7.3 doesn't have a finite representation in binary. The closest you can get is 2054767329987789/2**48 = 7.3+1/1407374883553280.

Take a look at http://docs.python.org/tutorial/floatingpoint.html for a further explanation. (It's on the Python website, but Java and C++ have the same "problem".)

The solution depends on what exactly your problem is:

  • If it's that you just don't like seeing all those noise digits, then fix your string formatting. Don't display more than 15 significant digits (or 7 for float).
  • If it's that the inexactness of your numbers is breaking things like "if" statements, then you should write if (abs(x - 7.3) < TOLERANCE) instead of if (x == 7.3).
  • If you're working with money, then what you probably really want is decimal fixed point. Store an integer number of cents or whatever the smallest unit of your currency is.
  • (VERY UNLIKELY) If you need more than 53 significant bits (15-16 significant digits) of precision, then use a high-precision floating-point type, like BigDecimal.
dan04
7.3 may not have a finite representation in binary, but I sure do get -7.3 when I try the same thing out in C++
wrongusername
wrongusername: No, you don't. It just displays that way. Use "%.17g" format (or better yet, "%.51g") to see the real answer.
dan04
I see. Thanks! :)
wrongusername
+5  A: 

You're running up against the precision limitation of type double.

Java.Math has some arbitrary-precision arithmetic facilities.

John at CashCommons
arbitrary precision arithmetic facilities such as...
Aly
http://java.sun.com/j2se/1.4.2/docs/api/java/math/package-summary.html
John at CashCommons
... such as the java.math package.http://java.sun.com/j2se/1.5.0/docs/api/java/math/BigDecimal.html
Matthew
+2  A: 

Computers store numbers in binary and can't actually represent numbers such as 33.333333333 or 100.0 exactly. This is one of the tricky things about using doubles. You will have to just round the answer before showing it to a user. Luckily in most applications, you don't need that many decimal places anyhow.

Jay Askren
I am doing some odds calculations I would prefer to have the highest precision possible. But I understand there are limitations
Aly
+1  A: 

Floating point numbers differ from real numbers in that for any given floating point number there is a next higher floating point number. Same as integers. There's no integer between 1 and 2.

There's no way to represent 1/3 as a float. There's a float below it and there's here's a float above it, and there's a certain distance between them. And 1/3 is in that space.

Apfloat for Java claims to work with arbitrary precision floating point numbers, but I've never used it. Probably worth a look. http://www.apfloat.org/apfloat_java/

A similar question was asked here before http://stackoverflow.com/questions/277309/java-floating-point-high-precision-library

Spike
+27  A: 

When you type 33.33333333333333, the value you get is actually the closest representable double-precision value, which is exactly:

33.3333333333333285963817615993320941925048828125

Dividing that by 100 gives:

0.333333333333333285963817615993320941925048828125

which also isn't representable as a double-precision number, so again it is rounded to the nearest representable value, which is exactly:

0.3333333333333332593184650249895639717578887939453125

When you print this value out, it gets rounded yet again to 17 decimal digits, giving:

0.33333333333333326
Stephen Canon
+1, for great maths!
Carl
Seriously, this should be the accepted answer imo. +1
Blindy
@Blindy: noooo! if this gets accepted, I'll miss my chance at the populist badge that I've always wanted! =)
Stephen Canon
To anyone who is reading this in the future and is puzzled as to why the answer has nothing to do with the question: some moderator decided to merge the question I (and others) had answered with this, rather different, question.
Stephen Canon
heh, thanks stephen. my mind spinned there for a sec
Halo
@Halo: on the plus side, the merge is what finally got me my populist badge =)
Stephen Canon
yeah:D another +1 by me, to your luck ;P
Halo
+3  A: 

You may want to look into using java's java.math.BigDecimal class if you really need precision math. Here is a good article from Oracle/Sun on the case for BigDecimal: http://blogs.sun.com/CoreJavaTechTips/entry/the_need_for_bigdecimal. While you can never represent 1/3 as someone mentioned, you can have the power to decide exactly how precise you want the result to be. setScale() is your friend.. :)

Ok, because I have way too much time on my hands at the moment here is a code example that relates to your question:

import java.math.BigDecimal;
/**
 * Created by a wonderful programmer known as:
 * Vincent Stoessel
 * [email protected]
 * on Mar 17, 2010 at  11:05:16 PM
 */
public class BigUp {

    public static void main(String[] args) {
        BigDecimal first, second, result ;
        first = new BigDecimal("33.33333333333333")  ;
        second = new BigDecimal("100") ;
        result = first.divide(second);
        System.out.println("result is " + result);
       //will print : result is 0.3333333333333333


    }
}

and to plug my new favorite language, Groovy, here is a neater example of the same thing:

import java.math.BigDecimal

def  first =   new BigDecimal("33.33333333333333")
def second = new BigDecimal("100")


println "result is " + first/second   // will print: result is 0.33333333333333
Vinny
+10  A: 

If you just want to process values as fractions, you can create a Fraction class which holds a numerator and denominator field.

Write methods for add, subtract, multiply and divide as well as a toDouble method. This way you can avoid floats during calculations.

EDIT: Quick implementation,

public class Fraction {

private int numerator;
private int denominator;

public Fraction(int n, int d){
    numerator = n;
    denominator = d;
}

public double toDouble(){
    return ((double)numerator)/((double)denominator);
}


public static Fraction add(Fraction a, Fraction b){
    if(a.denominator != b.denominator){
        double aTop = b.denominator * a.numerator;
        double bTop = a.denominator * b.numerator;
        return new Fraction(aTop + bTop, a.denominator * b.denominator);
    }
    else{
        return new Fraction(a.numerator + b.numerator, a.denominator);
    }
}

public static Fraction divide(Fraction a, Fraction b){
    return new Fraction(a.numerator * b.denominator, a.denominator * b.numerator);
}

public static Fraction multiply(Fraction a, Fraction b){
    return new Fraction(a.numerator * b.numerator, a.denominator * b.denominator);
}

public static Fraction subtract(Fraction a, Fraction b){
    if(a.denominator != b.denominator){
        double aTop = b.denominator * a.numerator;
        double bTop = a.denominator * b.numerator;
        return new Fraction(aTop - bTop, a.denominator + b.denominator);
    }
    else{
        return new Fraction(a.numerator - b.numerator, a.denominator);
    }
}

}
ViralShah
+1 for dedication to the cause! cheers
Aly
Surely `numerator` and `denominator` should be `int`s? Why would you want floating-point precision?
Samir Talwar
Guess it's not really necessary but it avoids casting in the toDouble function so the code reads better.
ViralShah
ViralShah: It also potentially introduces floating-point error when dealing with mathematical operations. Given that the point of this exercise is to avoid exactly that, it seems prudent to alter it.
Samir Talwar
Edited to use ints instead of doubles, for the reasons mentioned by Samir Talwar above.
ViralShah
A: 

Do not waste your efford using BigDecimal. In 99.99999% cases you don't need it. java double type is of cource approximate but in almost all cases, it is sufficiently precise. Mind that your have an error at 14th significant digit. This is really negligible!

To get nice output use:

System.out.printf("%.2f\n", total);
Maciek D.
Obviously he's worried about it in this case.
Jim Kiley
I think he is worried by the output, not the numerical precision. and BigDecimal would be no help if you eg. divide by three. It can even make things worse...
Maciek D.