Double type precision in java

H

haley

i am using java 6 sdk. I write a program like below:
public static void main(String[] args) {
double weight[][] = {
{ 0.0, 1.0, -3.0, 2.0, -0.3, -2.1, -1.0},
{ 1.0, 0.0, 1.0, 1.0, 3.0, -4.0, 0.0},
{-3.0, 1.0, 0.0, 0.0, 1.0, -1.0, -1.0},
{ 2.0, 1.0, 0.0, 0.0, 0.0, -2.4, 3.3},
{-0.3, 3.0, 1.0, 0.0, 0.0, 1.0, 1.0},
{-2.1, -4.0, -1.0, -2.4, 1.0, 0.0, -1.0},
{ 1.0, 0.0, -1.0, 3.3, 1.0, -1.0, 0.0}
};
double input[] = {1.000000 ,-1.000000 ,1.000000 ,-1.000000 ,1.000000
,-1.000000 ,1.000000};

double sum = 0.0;
double temp;
for (int i = 0; i < 7; i++ ){
sum = 0.0;
for (int j = 0; j < 7; j++ ){
temp = weight[j] * input[j];
sum += temp;
System.out.println(temp);

}
System.out.println("\nsum " + i + " = " + sum);
}

}

The output i get is this:
0.0
-1.0
-3.0
-2.0
-0.3
2.1
-1.0

sum 0 = -5.199999999999999
1.0
-0.0
1.0
-1.0
3.0
4.0
0.0

sum 1 = 8.0
-3.0
-1.0
0.0
-0.0
1.0
1.0
-1.0

sum 2 = -3.0
2.0
-1.0
0.0
-0.0
0.0
2.4
3.3

sum 3 = 6.699999999999999
-0.3
-3.0
1.0
-0.0
0.0
-1.0
1.0

sum 4 = -2.3
-2.1
4.0
-1.0
2.4
1.0
-0.0
-1.0

sum 5 = 3.3
1.0
-0.0
-1.0
-3.3
1.0
1.0
0.0

sum 6 = -1.2999999999999998

Why this happen?
why sumation of
1.0
-0.0
-1.0
-3.3
1.0
1.0
0.0
= -1.2999999999999998
this is definitely not correct!!!!
the answer should be -1.30000000000000
 
T

TechBookReport

haley said:
i am using java 6 sdk. I write a program like below:
public static void main(String[] args) {
double weight[][] = {
{ 0.0, 1.0, -3.0, 2.0, -0.3, -2.1, -1.0},
{ 1.0, 0.0, 1.0, 1.0, 3.0, -4.0, 0.0},
{-3.0, 1.0, 0.0, 0.0, 1.0, -1.0, -1.0},
{ 2.0, 1.0, 0.0, 0.0, 0.0, -2.4, 3.3},
{-0.3, 3.0, 1.0, 0.0, 0.0, 1.0, 1.0},
{-2.1, -4.0, -1.0, -2.4, 1.0, 0.0, -1.0},
{ 1.0, 0.0, -1.0, 3.3, 1.0, -1.0, 0.0}
};
double input[] = {1.000000 ,-1.000000 ,1.000000 ,-1.000000 ,1.000000
,-1.000000 ,1.000000};

double sum = 0.0;
double temp;
for (int i = 0; i < 7; i++ ){
sum = 0.0;
for (int j = 0; j < 7; j++ ){
temp = weight[j] * input[j];
sum += temp;
System.out.println(temp);

}
System.out.println("\nsum " + i + " = " + sum);
}

}

The output i get is this:
0.0
-1.0
-3.0
-2.0
-0.3
2.1
-1.0

sum 0 = -5.199999999999999
1.0
-0.0
1.0
-1.0
3.0
4.0
0.0

sum 1 = 8.0
-3.0
-1.0
0.0
-0.0
1.0
1.0
-1.0

sum 2 = -3.0
2.0
-1.0
0.0
-0.0
0.0
2.4
3.3

sum 3 = 6.699999999999999
-0.3
-3.0
1.0
-0.0
0.0
-1.0
1.0

sum 4 = -2.3
-2.1
4.0
-1.0
2.4
1.0
-0.0
-1.0

sum 5 = 3.3
1.0
-0.0
-1.0
-3.3
1.0
1.0
0.0

sum 6 = -1.2999999999999998

Why this happen?
why sumation of
1.0
-0.0
-1.0
-3.3
1.0
1.0
0.0
= -1.2999999999999998
this is definitely not correct!!!!
the answer should be -1.30000000000000

Google about floating point numbers. This isn't a Java issue, it's about
converting decimal values to binary...
 
H

haley

Thanks for TechBookReport suggestion. Now i know the limitation of
floating point number representation in computer.
That why this problem occur. Need to roundup the number in order to get
the proper answer by changing it to

BigDecimal sum = new BigDecimal(0.0);
double temp;
for (int i = 0; i < 7; i++ ){
sum = new BigDecimal(0.0);
for (int j = 0; j < 7; j++ ){

temp = weight[j] * input[j] ;
sum = sum.add(new BigDecimal(temp));
System.out.println(temp);

}
System.out.println("\nsum " + i + " = " + sum.setScale(1,
java.math.BigDecimal.ROUND_UP));
}

Thank you very much and happy new year 2007
 
L

Lew

haley said:
Thanks for TechBookReport suggestion. Now i know the limitation of
floating point number representation in computer.
That why this problem occur. Need to roundup the number in order to get
the proper answer by changing it to

BigDecimal sum = new BigDecimal(0.0);
double temp;
for (int i = 0; i < 7; i++ ){
sum = new BigDecimal(0.0);
for (int j = 0; j < 7; j++ ){

temp = weight[j] * input[j] ;
sum = sum.add(new BigDecimal(temp));
System.out.println(temp);

}
System.out.println("\nsum " + i + " = " + sum.setScale(1,
java.math.BigDecimal.ROUND_UP));
}


Please do not embed TAB characters in your postings.

Floating point and numerical analysis are very large, subtle and important
topics. Keep studying them. There is a whole lot more to learn.

For example, why did you use ROUND_UP instead of ROUND_HALF_EVEN? The latter
is usually preferable.

Also, do you need that much precision in your calculations, or only in the
display of your results?

Can you explain to someone learning the field what the difference is between
precision and accuracy?

- Lew
 
P

Patricia Shanahan

TechBookReport said:
haley said:
i am using java 6 sdk. I write a program like below:
public static void main(String[] args) {
double weight[][] = {
{ 0.0, 1.0, -3.0, 2.0, -0.3, -2.1, -1.0},
{ 1.0, 0.0, 1.0, 1.0, 3.0, -4.0, 0.0},
{-3.0, 1.0, 0.0, 0.0, 1.0, -1.0, -1.0},
{ 2.0, 1.0, 0.0, 0.0, 0.0, -2.4, 3.3},
{-0.3, 3.0, 1.0, 0.0, 0.0, 1.0, 1.0},
{-2.1, -4.0, -1.0, -2.4, 1.0, 0.0, -1.0},
{ 1.0, 0.0, -1.0, 3.3, 1.0, -1.0, 0.0}
};
double input[] = {1.000000 ,-1.000000 ,1.000000 ,-1.000000
,1.000000
,-1.000000 ,1.000000};

double sum = 0.0;
double temp;
for (int i = 0; i < 7; i++ ){
sum = 0.0;
for (int j = 0; j < 7; j++ ){
temp = weight[j] * input[j];
sum += temp;
System.out.println(temp);

}
System.out.println("\nsum " + i + " = " + sum);
}

}

The output i get is this:
0.0
-1.0
-3.0
-2.0
-0.3
2.1
-1.0

sum 0 = -5.199999999999999 ....

Google about floating point numbers. This isn't a Java issue, it's about
converting decimal values to binary...


There are actually two issues involved, one of which is very definitely
a Java issue.

The basic fact of rounding errors is a general issue with any finite
precision representation. It is particularly bad when there is a
mismatch between the floating point radix and the requirements. If there
is a requirement for exact representation of numbers that are all short
decimal fractions, use either BigDecimal or count, as an integer, the
number of tenths or hundredths or whatever.

On the other hand, most programming languages hide this issue away by
defaulting floating point output to a reasonable precision. Java, on the
other hand, by default preserves enough decimal digits to ensure that
reading the number back in would get the same internal number. That is
usually too many digits.

If the only problem is the output, look at DcimalFormat, or use
System.out.printf.

Patricia
 
J

John Ersatznom

Patricia said:
There are actually two issues involved, one of which is very definitely
a Java issue.

The basic fact of rounding errors is a general issue with any finite
precision representation. It is particularly bad when there is a
mismatch between the floating point radix and the requirements. If there
is a requirement for exact representation of numbers that are all short
decimal fractions, use either BigDecimal or count, as an integer, the
number of tenths or hundredths or whatever.

On the other hand, most programming languages hide this issue away by
defaulting floating point output to a reasonable precision. Java, on the
other hand, by default preserves enough decimal digits to ensure that
reading the number back in would get the same internal number. That is
usually too many digits.

If the only problem is the output, look at DcimalFormat, or use
System.out.printf.

If you look closely, the .999999-ending results were all from sums some
of whose inputs were rational with a non-power-of-2 denominator. It's
when you get tenths or other oddball decimals that weird things start
happening, because 0.2 (1/5) and 0.1 (1/10) and the like have no
terminating binary representation.

Now, there are, oddly, some -0.0s in the lists output. I'd guess it was
actually a subnormal -0.00000...0001 internally (and it's NOT always
outputting enough digits to exactly read it back in) except that many of
those are definitely products of power-of-2-denominator rationals -- in
fact, integers to the last one. Those shouldn't lose precision (unless
the numerator gets too big).

Unless for some reason it's actually trying to represent an actual value
of minus zero...
 
P

Patricia Shanahan

John said:
If you look closely, the .999999-ending results were all from sums some
of whose inputs were rational with a non-power-of-2 denominator. It's
when you get tenths or other oddball decimals that weird things start
happening, because 0.2 (1/5) and 0.1 (1/10) and the like have no
terminating binary representation.

Now, there are, oddly, some -0.0s in the lists output. I'd guess it was
actually a subnormal -0.00000...0001 internally (and it's NOT always
outputting enough digits to exactly read it back in) except that many of
those are definitely products of power-of-2-denominator rationals -- in
fact, integers to the last one. Those shouldn't lose precision (unless
the numerator gets too big).

Guessing that something as basic as Double.toString is not conforming to
its contract seems a little extreme to me. Also, the program contains
neither very small numbers, nor long chains of calculations, so I
don't see how it could produce a subnormal result.
Unless for some reason it's actually trying to represent an actual value
of minus zero...

Negative zero is an exactly representable value in Java's floating point
number system, as well as in IEEE 754, and that value is the only input
to Double.toString that is supposed to result in "-0.0".

There are many operations that result in a negative zero. For example,
the first "-0.0" output was the product of 0.0 and -1.0.

The JLS depends on IEEE 754 for specification of multiplication results:
"The result of a floating-point multiplication is governed by the rules
of IEEE 754 arithmetic:"

IEEE 754 specifies that the sign bit of a product is the exclusive or of
the operands' signs. 0.0 has sign bit zero, and -1.0 has sign bit 1, so
their product has sign bit 1. The magnitude is zero, so the product is -0.0.

Patricia
 
C

Chris Uppal

John said:
Now, there are, oddly, some -0.0s in the lists output. I'd guess it was
actually a subnormal -0.00000...0001 internally (and it's NOT always
outputting enough digits to exactly read it back in) except that many of
those are definitely products of power-of-2-denominator rationals -- in
fact, integers to the last one. Those shouldn't lose precision (unless
the numerator gets too big).

I don't think the representation of -0.0 is any kind of an issue here (-0.0 has
a perfectly good FP representation all of its own, so an "approximate" internal
representation is neither necessary nor permitted). To see a simpler version
of the same test that displays the same behaviour without any zeros:

public class Test
{
public static void
main(String[] args)
{
System.out.println(0.1D);
System.out.println(2.1D - 2.0D);
}
}

Which produces the output:
0.1
0.10000000000000009

(In this case the sum in the second println() is actually evaluated by the
compiler rather than at runtime, but that makes no difference to the
behaviour).


One way to think of why this happens:

The value 2.1 when represented as 64-bit floating point is "rounded" to the
nearest binary value, as it happens this is the value which (if printed at
perfect precision) would be
2.100000000000000088817841970012523233890533447265625
(That is just one a range of real numbers which all have the same
representation as a double, but it is the only one from that which is exactly
representable as a double).

Similarly the most precise representation of 2.0 is
2.
(;-)

But that of the result of the calculation (2.1 - 2.0) is:
0.100000000000000088817841970012523233890533447265625

For comparison, that of 0.1 is
0.1000000000000000055511151231257827021181583404541015625

Note the difference. This is part of the reason for the unexpected (by the OP)
results, and is also the reason why (2.1 - 2.0) != 0.1.

Also, when Java prints the double values, it chooses the real number with the
shortest string representation from the range of reals which all map the
same double value. In this case the nearest to:
0.100000000000000088817841970012523233890533447265625

is:
0.1

Whereas the nearest to:
0.1000000000000000055511151231257827021181583404541015625
is:
0.10000000000000009

Hence the output of the test program.

-- chris
 

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

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,535
Members
45,008
Latest member
obedient dusk

Latest Threads

Top