# BigDecimal and double arithmetic problems

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

1. ### RamonGuest

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

2. ### Martin GregorieGuest

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

3. ### Joshua CranmerGuest

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
4. ### RamonGuest

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

Ramon, Nov 2, 2008
5. ### Arne VajhÃ¸jGuest

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
6. ### LewGuest

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
7. ### LewGuest

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
8. ### RamonGuest

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
9. ### John B. MatthewsGuest

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

John B. Matthews, Nov 2, 2008
10. ### Hal RosserGuest

"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");

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
11. ### RamonGuest

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");
>
>
> 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
12. ### Paul SelibasGuest

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
13. ### LewGuest

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