# Loss of precision worry when using Math.pow()

Darryl Nortje

Ranch Hand

Posts: 140

posted 9 years ago

Hi guys,

Don't know if this question belongs in the advanced or beginners java section, so am putting in the intermediate section. Hope this is right, but please move if not.

I have a worry when it comes to floating point values loss of precision. Maybe you guys can help to substiate my worry, or provide a solution to it.

I'm writing a financial application that it working with decimal numbers. Therefore i can't use float or double. Use bigdecimal instead. I need to take 'a decimal value' to the power of 'another decimal value'.

There is the function Math.pow(double a, double b) that returns a double.

If I do this for example:

BigDecimal one = new BigDecimal("1.234");

BigDecimal two = new BigDecimal("1.5");

double answer = Math.pow(one.doubleValue(), two.doubleValue());

BigDecimal result = new BigDecimal(answer);

So my question is, will there be a loss of precision for the variable 'answer' when I call the Math.pow method?

Any comments?

cheers

Darryl

Don't know if this question belongs in the advanced or beginners java section, so am putting in the intermediate section. Hope this is right, but please move if not.

I have a worry when it comes to floating point values loss of precision. Maybe you guys can help to substiate my worry, or provide a solution to it.

I'm writing a financial application that it working with decimal numbers. Therefore i can't use float or double. Use bigdecimal instead. I need to take 'a decimal value' to the power of 'another decimal value'.

There is the function Math.pow(double a, double b) that returns a double.

If I do this for example:

BigDecimal one = new BigDecimal("1.234");

BigDecimal two = new BigDecimal("1.5");

double answer = Math.pow(one.doubleValue(), two.doubleValue());

BigDecimal result = new BigDecimal(answer);

So my question is, will there be a loss of precision for the variable 'answer' when I call the Math.pow method?

Any comments?

cheers

Darryl

Darryl Nortje

Ranch Hand

Posts: 140

posted 9 years ago

Just to expand a little....

When I do the above in the windows calculator I get

1.3707957192813231133678705725098

And when I run the above program in eclipse I get

1.370795719281323155058771590120159089565277099609375

So there is a descrepancy... Has loss of precision occured, or is windows calculator rounding off.

How would one do this if in fact loss of precision has occured in the Math.pow method call.

cheers

Darryl

When I do the above in the windows calculator I get

1.3707957192813231133678705725098

And when I run the above program in eclipse I get

1.370795719281323155058771590120159089565277099609375

So there is a descrepancy... Has loss of precision occured, or is windows calculator rounding off.

How would one do this if in fact loss of precision has occured in the Math.pow method call.

cheers

Darryl

Robert Hill

Ranch Hand

Posts: 94

posted 9 years ago

Maybe, run it and get the answer and compare it using a calculator that is accurate to as many places as you can find.

That specific example isn't all that complex, so it should be fairly accurate. It is equivilent to square root of 1.234^3. My Ti-85 graphing calculator returns 1.37079571928, which is precise enough for most applications.

Running the program with 1.5 on Linux the result returned is 1.3707957192813232, without using BigDecimal at the end, using it before the pow90 call is pointless. Is this precise? No, since the real result is irrational, you will never have precision. How precise you need, depends on the situation. 4 significant digits might be peachy in one case, and death or massive problems in another.

[ September 13, 2006: Message edited by: Robert Hill ]

That specific example isn't all that complex, so it should be fairly accurate. It is equivilent to square root of 1.234^3. My Ti-85 graphing calculator returns 1.37079571928, which is precise enough for most applications.

Running the program with 1.5 on Linux the result returned is 1.3707957192813232, without using BigDecimal at the end, using it before the pow90 call is pointless. Is this precise? No, since the real result is irrational, you will never have precision. How precise you need, depends on the situation. 4 significant digits might be peachy in one case, and death or massive problems in another.

[ September 13, 2006: Message edited by: Robert Hill ]

Darryl Nortje

Ranch Hand

Posts: 140

posted 9 years ago

Hi there,

Thanks for the reply. I'm not quite sure I follow. Albeit that my example might be bad, there probably is instances where even the rounding off wouldn't help. If you look at example below, it is much much simpler, yet even with rounding gives the totally incorrect result.

double x = 1.0;

double y = 0.8;

System.out.println(x - y);

prints out 0.19999999999999996.

So perhaps ignore my example and how close it is to the real answer. There might be some doubles when you call Math.pow() that also give totally incorrect results that not even rounding will help.

So is there some other way (using BigDecimal) that you can do something similar to Math.pow() without loosing precision.

cheers

Darryl

Thanks for the reply. I'm not quite sure I follow. Albeit that my example might be bad, there probably is instances where even the rounding off wouldn't help. If you look at example below, it is much much simpler, yet even with rounding gives the totally incorrect result.

double x = 1.0;

double y = 0.8;

System.out.println(x - y);

prints out 0.19999999999999996.

So perhaps ignore my example and how close it is to the real answer. There might be some doubles when you call Math.pow() that also give totally incorrect results that not even rounding will help.

So is there some other way (using BigDecimal) that you can do something similar to Math.pow() without loosing precision.

cheers

Darryl

posted 9 years ago

First you say that you are deliberately not using float and double because you don't want to loose precision, but then you show a piece of code that does convert your BigDecimals to doubles and back. So you are using doubles, even though you just said you didn't want to use them. Ofcourse the precision will not be better than what double provides if you do that.

The types float and double store floating point numbers in binary form. Just like you cannot express some numbers exactly in decimal form, such as 1/3 = 0.33333..., there are numbers that you cannot express exactly in binary form.

The number 0.2 in binary form is written as: 0.00110011001100110011... Note the recurring pattern, just like 0.3333... in decimal; the number just can't be represented exactly in binary form.

Try this instead of your code:

Surprise, surprise! It prints 0.200000 instead of 0.19999999999999996. That's because the formatter that printf() uses rounds the number correctly.

The result "0.19999...96" isn't "totally incorrect". It's correct in the context of the IEEE 754 specification for floating point numbers.

[ September 13, 2006: Message edited by: Jesper Young ]

The types float and double store floating point numbers in binary form. Just like you cannot express some numbers exactly in decimal form, such as 1/3 = 0.33333..., there are numbers that you cannot express exactly in binary form.

The number 0.2 in binary form is written as: 0.00110011001100110011... Note the recurring pattern, just like 0.3333... in decimal; the number just can't be represented exactly in binary form.

Try this instead of your code:

Surprise, surprise! It prints 0.200000 instead of 0.19999999999999996. That's because the formatter that printf() uses rounds the number correctly.

The result "0.19999...96" isn't "totally incorrect". It's correct in the context of the IEEE 754 specification for floating point numbers.

[ September 13, 2006: Message edited by: Jesper Young ]