BigDecimal and double arithmetic problems

R

Ramon

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.
 
M

Martin Gregorie

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.
 
J

Joshua Cranmer

Ramon said:
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.
 
R

Ramon

Martin said:
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?
 
A

Arne Vajhøj

Ramon said:
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
 
L

Lew

Ramon said:
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?
 
R

Ramon

Arne said:
> 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!
 
J

John B. Matthews

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>
 
H

Hal Rosser

Ramon said:
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
 
R

Ramon

Hal said:
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
 
P

Paul Selibas

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.
 
L

Lew

Paul said:
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.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,014
Latest member
BiancaFix3

Latest Threads

Top