BigDecimal and double arithmetic problems

Discussion in 'Java' started by Ramon, Nov 2, 2008.

  1. Ramon

    Ramon Guest

    Hi,

    I had first a problem when using double primitive data type. I've tried
    to add to double numbers but the result was technically incorrect. Here
    is a snippet that was taken from the program:

    double a = -0.7;
    double b = 0.2;
    System.err.println( a + b ); // outputs -0.49999999999999994 instead of -0.5


    I've also tried to use BigDecimal but still no luck. Here is part of
    the program:

    BigDecimal a = new BigDecimal(-0.7);
    BigDecimal b = new BigDecimal(0.2);
    System.err.println( a.add(b) ); // unprecise output

    So, my question is: Is there a way to perform an accurate addition of
    -0.7 and 0.2 in Java?

    Thanks.
     
    Ramon, Nov 2, 2008
    #1
    1. Advertising

  2. On Sun, 02 Nov 2008 14:06:48 +0100, Ramon wrote:

    > Hi,
    >
    > I had first a problem when using double primitive data type. I've tried
    > to add to double numbers but the result was technically incorrect. Here
    > is a snippet that was taken from the program:
    >
    > double a = -0.7;
    > double b = 0.2;
    > System.err.println( a + b ); // outputs -0.49999999999999994 instead of
    > -0.5
    >

    This is scarcely surprising: its extremely unlikely that double can
    represent either literal exactly. This isn't Java-specific - any
    programming language will do the same.

    Programs must be written to recognise this fact:

    if (a == b) // WRONG
    if (Math.abs(a - b) < 0.01) // RIGHT

    but in the second example the acceptable difference (0.01 in this case)
    depends on the precision of the numbers you're calculating with.
    Similarly, you may need to round output values when you format them.

    If you don't know this then you don't understand floating point numbers.


    --
    martin@ | Martin Gregorie
    gregorie. | Essex, UK
    org |
     
    Martin Gregorie, Nov 2, 2008
    #2
    1. Advertising

  3. Ramon wrote:
    > Hi,
    >
    > I had first a problem when using double primitive data type. I've tried
    > to add to double numbers but the result was technically incorrect. Here
    > is a snippet that was taken from the program:
    >
    > double a = -0.7;
    > double b = 0.2;
    > System.err.println( a + b ); // outputs -0.49999999999999994 instead
    > of -0.5


    Actually, the result is more correct than you think it is. A computer
    cannot store 0.7 or 0.2 accurately; what it instead stores is a number
    that is correct to within 2^-53 (at least for double types).

    Adding -0.7 + 0.2 is actually therefore adding two numbers who are
    within 2^-53 of those numbers, so the result is within 2^-52 of -0.5. In
    this case, the answer is not as close as it could be to -0.5--off by the
    last binary digit, in fact.

    So, it is in fact technically correct once you understand what the
    computer is really doing.

    > I've also tried to use BigDecimal but still no luck. Here is part of
    > the program:
    >
    > BigDecimal a = new BigDecimal(-0.7);
    > BigDecimal b = new BigDecimal(0.2);
    > System.err.println( a.add(b) ); // unprecise output


    It's not imprecise. It's doing [the binary representation of -0.7 within
    2^-53] + [the binary representation of 0.2 within 2^-53] without losing
    any precision: it treats each approximate number as being perfectly
    precise, and, as such, returns a more precise version of the output.

    Note that the loss in precision occurs at compile-time by merely writing
    -0.7. Java will store the closest double representation to -0.7, which
    is what BigDecimal is treating as an infinitely-precise number. If you
    want a more precise representation, you have to give it in a that isn't
    translated by the Java compiler... maybe you might want a String?

    > So, my question is: Is there a way to perform an accurate addition of
    > -0.7 and 0.2 in Java?


    The number is accurate to within 2^-52, which gives it an error of about
    2^-51. I'd say that's accurate enough for me.

    --
    Beware of bugs in the above code; I have only proved it correct, not
    tried it. -- Donald E. Knuth
     
    Joshua Cranmer, Nov 2, 2008
    #3
  4. Ramon

    Ramon Guest

    Martin Gregorie wrote:
    > This is scarcely surprising: its extremely unlikely that double can
    > represent either literal exactly. This isn't Java-specific - any
    > programming language will do the same.
    >
    > Programs must be written to recognise this fact:
    >
    > if (a == b) // WRONG
    > if (Math.abs(a - b) < 0.01) // RIGHT
    >
    > but in the second example the acceptable difference (0.01 in this case)
    > depends on the precision of the numbers you're calculating with.
    > Similarly, you may need to round output values when you format them.
    >
    > If you don't know this then you don't understand floating point numbers.
    >
    >


    Yes I know that the mother of all problems is the CPU's floating point
    numerical system. But is there a way how one can add -0.7 with 0.2 and
    the answer is exactly -0.5?
     
    Ramon, Nov 2, 2008
    #4
  5. Ramon wrote:
    > Martin Gregorie wrote:
    >> This is scarcely surprising: its extremely unlikely that double can
    >> represent either literal exactly. This isn't Java-specific - any
    >> programming language will do the same.
    >> Programs must be written to recognise this fact:
    >>
    >> if (a == b) // WRONG
    >> if (Math.abs(a - b) < 0.01) // RIGHT
    >>
    >> but in the second example the acceptable difference (0.01 in this
    >> case) depends on the precision of the numbers you're calculating with.
    >> Similarly, you may need to round output values when you format them.
    >>
    >> If you don't know this then you don't understand floating point numbers.
    >>
    >>

    >
    > Yes I know that the mother of all problems is the CPU's floating point
    > numerical system. But is there a way how one can add -0.7 with 0.2 and
    > the answer is exactly -0.5?


    Try with BigDecimal *and* use the construtor that takes a String
    as argument instead of the one that takes a double.

    Arne
     
    Arne Vajhøj, Nov 2, 2008
    #5
  6. Ramon

    Lew Guest

    Ramon wrote:
    > I had first a problem when using double primitive data type. I've tried
    > to add to double numbers but the result was technically incorrect. Here
    > is a snippet that was taken from the program:
    >
    > double a = -0.7;
    > double b = 0.2;
    > System.err.println( a + b ); // outputs -0.49999999999999994 instead
    > of -0.5


    <http://docs.sun.com/source/806-3568/ncg_goldberg.html>

    --
    Lew
     
    Lew, Nov 2, 2008
    #6
  7. Ramon

    Lew Guest

    Ramon wrote:
    > Yes I know that the mother of all problems is the CPU's floating point
    > numerical system. But is there a way how one can add -0.7 with 0.2 and
    > the answer is exactly -0.5?


    In decimal arithmetic accurate to, say, 6 places, is there a way to add 1/3
    and 1/7 so the answer is exactly 10/21?

    --
    Lew
     
    Lew, Nov 2, 2008
    #7
  8. Ramon

    Ramon Guest

    Arne Vajhøj wrote:
    > Try with BigDecimal *and* use the construtor that takes a String
    > as argument instead of the one that takes a double.
    >
    > Arne


    That did the trick. Thanks Arne!
     
    Ramon, Nov 2, 2008
    #8
  9. In article <gekc6m$2n5$>, Lew <> wrote:

    > Ramon wrote:
    > > Yes I know that the mother of all problems is the CPU's floating point
    > > numerical system. But is there a way how one can add -0.7 with 0.2 and
    > > the answer is exactly -0.5?

    >
    > In decimal arithmetic accurate to, say, 6 places, is there a way to add 1/3
    > and 1/7 so the answer is exactly 10/21?


    An excellent rhetorical question, as it suggests rational arithmetic as
    one approach to the OP's problem:

    <http://jscience.org/api/org/jscience/mathematics/number/Rational.html>

    <code>
    import org.jscience.mathematics.number.Rational;

    public class RationalTest {

    public static void main(String[] args) {
    Rational a = Rational.valueOf(-7, 10);
    Rational b = Rational.valueOf(2, 10);
    Rational sum = a.plus(b);
    System.out.println(sum);
    System.out.println(sum.doubleValue());
    a = Rational.valueOf(1, 3);
    b = Rational.valueOf(1, 7);
    sum = a.plus(b);
    System.out.println(sum);
    System.out.println(sum.doubleValue());
    }
    }
    </code>
    <console>
    -1/2
    -0.5
    10/21
    0.47619047619047616
    <.console>
    --
    John B. Matthews
    trashgod at gmail dot com
    http://home.roadrunner.com/~jbmatthews/
     
    John B. Matthews, Nov 2, 2008
    #9
  10. Ramon

    Hal Rosser Guest

    "Ramon" <> wrote in message
    news:gek8l4$nv8$...
    > Hi,
    > BigDecimal a = new BigDecimal(-0.7);
    > BigDecimal b = new BigDecimal(0.2);
    > System.err.println( a.add(b) ); // unprecise output
    >
    > So, my question is: Is there a way to perform an accurate addition
    > of -0.7 and 0.2 in Java?


    Yes,
    Use the BigDecimal constructor that accepts a String
    In other words, put quotes around the args

    BigDecimal a = new BigDecimal("-0.7");
    BigDecimal b = new BigDecimal("0.2");

    System.out.println(s.add(b));

    This will give ya the exactness for which you search.

    the way you did it, the impreciseness of the double is used to create the
    BigDecimal objects,
    but this way, its exact. Using the String arg usually works better for
    BigDecimal.

    Hope this helps
     
    Hal Rosser, Nov 3, 2008
    #10
  11. Ramon

    Ramon Guest

    Hal Rosser wrote:
    > "Ramon" <> wrote in message
    > news:gek8l4$nv8$...
    >> Hi,
    >> BigDecimal a = new BigDecimal(-0.7);
    >> BigDecimal b = new BigDecimal(0.2);
    >> System.err.println( a.add(b) ); // unprecise output
    >>
    >> So, my question is: Is there a way to perform an accurate addition
    >> of -0.7 and 0.2 in Java?

    >
    > Yes,
    > Use the BigDecimal constructor that accepts a String
    > In other words, put quotes around the args
    >
    > BigDecimal a = new BigDecimal("-0.7");
    > BigDecimal b = new BigDecimal("0.2");
    >
    > System.out.println(s.add(b));
    >
    > This will give ya the exactness for which you search.
    >
    > the way you did it, the impreciseness of the double is used to create the
    > BigDecimal objects,
    > but this way, its exact. Using the String arg usually works better for
    > BigDecimal.
    >
    > Hope this helps
    >
    >

    thanks
     
    Ramon, Nov 3, 2008
    #11
  12. Ramon

    Paul Selibas Guest

    On Nov 2, 3:06 pm, Ramon <> wrote:
    > Hi,
    >
    > I had first a problem when using double primitive data type.  I've tried
    > to add to double numbers but the result was technically incorrect.  Here
    > is a snippet that was taken from the program:
    >
    > double a = -0.7;
    > double b = 0.2;
    > System.err.println( a + b );    // outputs -0.49999999999999994 instead of -0.5
    >
    > I've also tried to use BigDecimal but still no luck.  Here is part of
    > the program:
    >
    > BigDecimal a = new BigDecimal(-0.7);
    > BigDecimal b = new BigDecimal(0.2);
    > System.err.println( a.add(b) );  // unprecise output
    >
    > So, my question is:  Is there a way to perform an accurate addition of
    > -0.7 and 0.2 in Java?
    >
    > Thanks.


    Just use BigDecimal.valueOf(double) to get the BigDecimal with the
    precision you want.
    Then you can do what you like with that BigDecimal.
     
    Paul Selibas, Nov 3, 2008
    #12
  13. Ramon

    Lew Guest

    Paul Selibas wrote:
    > Just use BigDecimal.valueOf(double) to get the BigDecimal with the
    > precision you want.


    This is not correct, and is exactly what was causing the problem.
    'BigDecimal.valueOf(double)' cannot introduce precision absent in the double,
    which is why everyone else is recommending the constructor with the String
    argument.

    --
    Lew
     
    Lew, Nov 3, 2008
    #13
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Forrest Hump
    Replies:
    4
    Views:
    24,286
    Mark Meyer
    Aug 22, 2003
  2. Sydex
    Replies:
    12
    Views:
    6,505
    Victor Bazarov
    Feb 17, 2005
  3. M.-A. Lemburg
    Replies:
    3
    Views:
    563
    M.-A. Lemburg
    Apr 4, 2005
  4. Replies:
    3
    Views:
    1,233
  5. Stanimir Stamenkov
    Replies:
    4
    Views:
    2,591
    Stanimir Stamenkov
    Jul 18, 2008
Loading...

Share This Page