Java mathematical function precision

I encoded some floating-point algorithm and could not get the expected results (actually I am trying to port my old Java program to Java). Still new to Java, 2 months !;) So I did some quick tests on a math function and found this:

System.out.printf("Math.PI^1 = %.22f MAth.PI^10 = %.22f \n", Math.pow(Math.PI, 1.0), Math.pow(Math.PI, 10.0)); 

And the conclusion is as follows:

 Math.PI^1 = 3.1415926535897930000000 MAth.PI^10 = 93648.0474760829800000000000 

As a reference, the calculator gives pi ^ 10 as follows:

 93,648.047476083020973716690184919 

which I expected to receive. Minor error: Ok, since the output from the mathematical function C (Cygwin environment) gives the following:

 printf("M_PI^1 = %.22f M_PI^10 = %.22f \n", pow(M_PI,1.0), pow(M_PI, 10.0)); $ ./a.exe M_PI^1 = 3.1415926535897931159980 M_PI^10 = 93648.0474760829820297658443 

Am I missing something critical? Or have I not specified any Java configuration? Yes, I searched and found the StrictMath and strictfp keywords to use. But still they give the same result in Java.

+7
java
source share
5 answers

float has about 7.2 significant decimal digits.
double has about 15.9 significant decimal digits.

Your examples have the same 16 first significant decimal digits. This means that both parts of the code get exactly the same binary result . Suppose your C compiler uses the same IEEE standard for 64-bit floats as java.

The difference that you see after these 16 digits does not come from how the mathematical operation is performed, and does not arise due to a rounding error, but from how various printing functions deal with the conversion from binary double to decimal text.

+7
source share

The fastest way to get the correct answer is to use Wolfram alpha pi ^ 10 , this will give the value 93648.04747608302097371669018491934563599815727551469412705244, probably more numbers could be obtained if necessary. We see that cygwin C code is correct for only 15 digits

 93648.0474760829820297658443 C 93648.04747608298 Java 93648.04747608302097371669018491934563599815727551469412705244 93648.047476082984468098606014523496270846023460034084392213341627 

therefore, you have exactly the same accuracy on both systems. You expect the same accuracy as the likelihood of using the IEEE 754 double precision floating point. You could say that the answer to Java is better, as it does not give a false sense of precision by displaying more numbers.

If you are not specifically interested in calculating the digits of the pi task or other number theory, then an accuracy of 16 digits will satisfy your needs. I have never seen an application where BigDecimal has proven useful, and it takes a lot of work.

BigDecimal solution will be

  MathContext mc = MathContext.DECIMAL128; BigDecimal pi = new BigDecimal("3.141592653589793238462643383279503",mc); BigDecimal res = pi.pow(10, mc); out.println(pi); out.println(res); 

In this case, a specific MathContext is used, the most accurate predefined one. If numbers are approximate, like pi, it is better to specify a MathContext. The only time you really want to use BigDecimal without a MathContext is that if your values ​​are accurate, I don't encounter the time when you want to use this.

We use a string constructor with a value obtained from Wolfram alpha and MathContext to capture precision. We also use the same MathContext when calculating power. The result of this is

 3.141592653589793238462643383279503 93648.04747608302097371669018491938 

if we compare this with the actual result, which ends in 934, we see that the result has an error in the last digit. Typically, you expect most math algorithms to be correct within the same unit of last place, pow is a little worse with a 2 ulp error. Using MathContext means that we do not display false incorrect numbers.

+3
source share

Yes. Java and C both suck in floating point numbers. You should really use floating points for estimates, not for exact values. Anything above the currency and you will have some inconsistencies.

If you need precision, you will need to use BigDecimal. You want to test it for performance, but you should get the answers you are looking for.

+2
source share

Use the BigDecimal class if you need precision. float and double are subject to rounding errors:

 BigDecimal test = new BigDecimal(Math.PI); System.out.println(test.pow(10)); 

Output:

 93648.047476082984468098606014523496270846023460034084392213341627785026090824331731984972528115769903975226563675097646096540840763090025329795404848362261074645725271801449534201154156882626448229305882846309041714031912085482167747513529870413304782789850546982088436194217303991296601721838481502870491848946572115751298158673535944440534837506339231012720875785869801057751354819447705578284437850332884027121079143234804533501909778030213894750577452441575587727129459381103515625 
+1
source share

You see these roundings because the double type is implemented in java ( JLS ) according to the IEEE 754 standard . Which in turn:

gives an accuracy of 15-17 significant decimal digits

So the accuracy you see is in line with the standard. As suggested in other answers, you can use BigDecimal for higher precision.

+1
source share

All Articles