math.pow rounding problem

J

Jimmy zhang

Hi, I tried Math.pow(10,-21) and expected to see 1e-21.
But instead I saw 1.00000000001e-21. what should I
do to get rid of the trailing 0.0000000000001??

Thanks,
Jimmy
 
V

VisionSet

Jimmy zhang said:
Hi, I tried Math.pow(10,-21) and expected to see 1e-21.
But instead I saw 1.00000000001e-21. what should I
do to get rid of the trailing 0.0000000000001??

Floating point arithemtic loses accuracy when dealing with decimal values,
because a floating point number can only approximate the decimal equivalent.

If you know your result will be in the int range ie -2^31 to 2^31
then do this:

int result = (int) Math.round(Math.pow(10, exp))

or for results in the range -2^63 to 2^63

long result = Math.round(Math.pow(10, exp))

if you wanted to keep it as a double then:

double result = Math.rint(Math.pow(10, exp))
 
J

Jimmy zhang

what if the result is actually a float or double?
I am trying implement my own parseDouble and parseFloat function ??
 
M

Mark Thornton

Jimmy said:
what if the result is actually a float or double?
I am trying implement my own parseDouble and parseFloat function ??

I suggest that you leave that task to the experts, which you clearly are
not. Correctly implementing parseDouble is rather more complex than it
might appear at first sight. If you want to learn, then some courses in
numerical analysis and the behaviour of binary floating point arithmetic
would be a good start.

Mark Thornton
 
B

Boudewijn Dijkstra

Jimmy zhang said:
what if the result is actually a float or double?
I am trying implement my own parseDouble and parseFloat
function ??

Powers of ten above a certain value will no longer fit into a float or double,
simply because representing the value in a binary
sign-exponent-significand-representation will take too many bits of the
significand.
 
A

Albert Greinöcker

I think these kind of problems could be solved with class
java.math.BigDecimal...
 
P

Patricia Shanahan

Jimmy said:
Hi, I tried Math.pow(10,-21) and expected to see 1e-21.
But instead I saw 1.00000000001e-21. what should I do to
get rid of the trailing 0.0000000000001??

Thanks, Jimmy

1e-21 cannot be exactly represented in double, because
doubles are finite-width binary fractions. It is similar to
the problem of representing 2/3 as a finite-width decimal
number.

The default output for Java prints enough digits to
distinguish the result from any other double, which may
include digits that are really meaningless. If the precision
is sufficient, and it is just a cosmetic problem, you could
use DecimalFormat to drop meaningless digits on output.

If you need exact representation of exact decimal fractions,
such as 1e-21, consider BigDecimal.

Patricia
 
B

Babu Kalakrishnan

[snip]

Welcome back Patricia. This group certainly missed your expertise during
the past couple of years. Hope to see you here more often.

BK
 
M

Michiel Konstapel

[snip]

Welcome back Patricia. This group certainly missed your expertise during
the past couple of years. Hope to see you here more often.


Hey, I didn't even notice it was Patricia until I read your post, Babu.
Just tacking on my "nice to see you again, Patricia" :)

Regards,
Michiel
 
C

Chris Smith

Patricia Shanahan said:
1e-21 cannot be exactly represented in double, because
doubles are finite-width binary fractions. It is similar to
the problem of representing 2/3 as a finite-width decimal
number.

That's certainly the easy answer. I wonder if it's right. When the
printed result is 1.0000000000000001E-21, I'm forced to conclude that
either of two things are true:

a) There is another possible double value that is closer to 1e-21 than
the one returned by Math.pow, and the extra digits were needed to
resolve the ambiguity. In that case, it appears Math.pow is broken,
since it should have returned that other double that's closer to 1e-21.

b) The double value returned by Math.pow is closer to 1e-21 than any
other possible double value. In that case, Double.toString is broken
since it didn't need to output those extra digits.

Where am I getting this wrong? Or am I?

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
P

Paul van Rossem

That's certainly the easy answer. I wonder if it's right. When the
printed result is 1.0000000000000001E-21, I'm forced to conclude that
either of two things are true:

a) There is another possible double value that is closer to 1e-21 than
the one returned by Math.pow, and the extra digits were needed to
resolve the ambiguity. In that case, it appears Math.pow is broken,
since it should have returned that other double that's closer to 1e-21.

b) The double value returned by Math.pow is closer to 1e-21 than any
other possible double value. In that case, Double.toString is broken
since it didn't need to output those extra digits.

Where am I getting this wrong? Or am I?
I think you forget that first the integer arguments of the call (10 and
21) must also be rounded to the nearest double. So there you start with
small rounding errors, that are then amplified by the power algorithm.

Paul.
 
B

Babu Kalakrishnan

Paul said:
I think you forget that first the integer arguments of the call (10 and
21) must also be rounded to the nearest double. So there you start with
small rounding errors, that are then amplified by the power algorithm.

Actually there are _no_ rounding errors in the conversion of the integer
arguments as such (all numbers in the "int" range are exactly
representable by a Double since the mantissa of a double has far more
number of significant bits).

However, your point that the error is due to overflows/underflows is
probably valid. The pow algorithm does use several coefficients for
computing log2(x) several of which are non-exact, and this probably
contributes to the error.

Whether there is a better algorithm that can always provide the
_nearest_ double as the result (which I think was Chris's point) - I
think it is upto the mathematicians to debate.. (Patricia ?). But as far
as I can see, the error is exactly 1 bit at the LSB. (1E-21 as encoded
by the string parser has a mantissa of 0x12e3b40a0e9b4f whereas the
result returned has a mantissa of 0x12e3b40a0e9b50.

And as to which of the two presumbtions is correct (point (a) or (b) in
Chris's post), I think it is case (a) - which can be verified by
creating BigDecimals from the two doubles and checking which is nearer
to 1E-21.

BK
 
B

Boudewijn Dijkstra

Paul van Rossem said:
I think you forget that first the integer arguments of the call (10 and 21)
must also be rounded to the nearest double. So there you start with
small rounding errors, that are then amplified by the power algorithm.

Both range and precision of doubles are enough (by far) to precisely represent
10 and 21.
 
C

Chris Uppal

Babu said:
And as to which of the two presumbtions is correct (point (a) or (b) in
Chris's post), I think it is case (a) - which can be verified by
creating BigDecimals from the two doubles and checking which is nearer
to 1E-21.

For completeness:

The "correct" answer is (of course):
1 / (10 ** 21)

The answer given by pow() is, decoding the bits of the double:
5316911983139664 / (2 ** 122)

The value of 1.0e-21D is (again decoding the bits of the double):
5316911983139663 / (2 ** 122)

The same three values in decimal format (fully expanded):
exact: 0.000 000 000 000 000 000 001
pow(): 0.000 000 000 000 000 000 001 000 000 000 000 000
095 616 548 359 462 372 671 727 780 467 234 839
207 896 907 042 893 985 817 499 924 451 112 747
192 382 812 5
1e-21: 0.000 000 000 000 000 000 000 999 999 999 999 999
907 537 452 227 896 371 396 729 934 511 675 530
756 910 417 959 359 982 376 099 651 446 565 985
679 626 464 843 75

The exact answer is not exactly representable as a Java double, but lies
between the answer given by pow() and the value of 1.0e-21D. If you do the
arithmetic, you'll see that the exact value actually lies slightly closer to
the value of 1.0e-21D (by about 3%), so pow() is not answering the closest
possible double to the mathematically correct answer. So in that sense the
answer is "wrong", and Chris's option (a) holds. However I wouldn't follow him
in calling pow() "broken" since it is actually returning the /second/ closest
double to the correct answer (the one just above rather than the one just
below), and that is acceptable according the contract of pow() since it is
within one "ulp" of the mathematically correct answer.

-- chris
 
B

Boudewijn Dijkstra

Chris Uppal said:
Babu Kalakrishnan wrote:

[...] So in that sense the answer is "wrong", and Chris's option
(a) holds. However I wouldn't follow him in calling pow()
"broken" since it is actually returning the /second/ closest
double to the correct answer (the one just above rather than the
one just below), and that is acceptable according the contract of
pow() since it is within one "ulp" of the mathematically correct
answer.

So all is swell now, and we can continue to live happily ever after.
 
C

Chris Smith

Chris Uppal said:
answer is "wrong", and Chris's option (a) holds. However I wouldn't follow him
in calling pow() "broken" since it is actually returning the /second/ closest
double to the correct answer (the one just above rather than the one just
below), and that is acceptable according the contract of pow() since it is
within one "ulp" of the mathematically correct answer.

That's the answer I was missing. I should have read the API docs before
asking, I suppose, but I just assumed that I already knew what they
said. Turns out I didn't.

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 

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,769
Messages
2,569,576
Members
45,054
Latest member
LucyCarper

Latest Threads

Top