How to get greater precision for PI using the Harmonic Series - java

trying to get back into Java and have decided to tackle PI. So I made this program based on the Harmonic Series:
public static void main(String [] args)
{
double denominator = 1.0;
double tempValue;
double PI = 0.0;
// End point for program
double stopPoint = 1234.5;
for( int i = 1; i < stopPoint; i++ )
{
tempValue = Math.sqrt( (1/(denominator*denominator))/6 );
PI = PI + tempValue;
denominator = denominator + 1.0;
}
System.out.println( "PI = " + PI );
The application prints this:
PI = 3.1417306496998294
So you can see its mildly working. But when I change the stopPoint value any more I'm not getting a change in precision, at all.
For example changing it to 1234.75 gives the same answer - or perhaps print can't display the exact value? If so what is the best way to print out values like these?
Thanks
EDIT
I've added this code as its a change to the code posted above. Some of the changes include the use of Big Decimal and the inclusion of a while loop instead of a for.
import java.math.BigDecimal;
import java.math.MathContext;
public class MyPI
{
final static BigDecimal ONE = new BigDecimal(1);
final static BigDecimal SIX = new BigDecimal(6);
public static void main(String [] args)
{
BigDecimal deno, temp, tempPI;
int start, end;
start = 1;
end = 500000;
temp = new BigDecimal(0);
// Starting denominator point
deno = ONE;
while( start < end )
{
// Without precision and rounding mode, it will try to return a
// never ending number
temp = temp.add( ONE.divide(deno.pow(2),MathContext.DECIMAL64) );
deno = deno.add(ONE);
start = start + 1;
}
tempPI = temp.multiply(SIX);
// Need to convert to double for square root
double PI = Math.sqrt( tempPI.doubleValue() );
System.out.println( "PI: " + PI );
}
}
This produces the following result:
PI: 3.1415907437318054
Thanks all for the help - will probably add a timer to track how long it takes to do this.

I've been using the BigDecimal type instead of the double, but I have hit a bit of a roadblock—the square root.
Don't take the square root of each term. As shown in this example, add the terms of the series, which has the exact sum π2/6. When your loop terminates, multiply by six and then take a single square root.

If you want more precision, you can use Java's BigDecimal.

Use Java BigDecimal instead of Double that has a limited precision.

The BigDecimal class can give you "arbitrary-precision signed decimal numbers", which is what you want in this case, although the instances of BigDecimal are a little more tricky to work with than literals, the class actually works quickly and can be used to do what you need fairly accurately.
Just for your information, though, using the harmonic series to calculate Pi is pretty inefficient, but I understand doing it as a fun program or to learn something new.

Related

Why does my code which uses the Ramanujan formula for calculating the value of pi not produce correct values?

package ValueOfPi;
import java.math.*;
import java.util.Scanner;
public class RamanujanAlgorithm {
public static void main(String[] args) {
double constant = 0.0002885855652;
Scanner sc = new Scanner(System.in);
System.out.println("Enter limit of summation : ");
int limit = sc.nextInt();
BigDecimal pireci = new BigDecimal("0.00");
for(int n = 0; n<=limit;n++) {
BigDecimal summation = new BigDecimal((factorial(4*n)/Math.pow(factorial(n), 4))*
(26390*n + 1103)/Math.pow(396, 4*n));
pireci = pireci.add(summation);
}
pireci = pireci.multiply(BigDecimal.valueOf(constant));
BigDecimal pi = BigDecimal.ONE.divide(pireci,pireci.scale(),RoundingMode.HALF_UP);
System.out.println(pi);
}
public static long factorial(int num) {
long factorial = 1;
for(int i = 2; i<=num; i++) {
factorial = factorial*i;
}
return factorial;
}
}
SOURCE OF FORMULA -
The Ramanujan Formula I used is -
https://en.wikipedia.org/wiki/Pi#Rapidly_convergent_series
The first formula you see is the one used by me...
EXPLANATION OF MY CODE
In my code, I have first stored the value of root(8)/9801 in the variable named constant.
Then I have taken user input for the upper limit of the summation...
Then the loop works like the Sigma, where everything on the right side of the sigma in the formula is computed and stored in the BigDecimal pireci and at last after coming out of the loop, I have multiplied pireci with the variable constant
The result is stored in pireci.
So, for getting pi, I find the reciprocal of pireci(because the formula gives me value of 1/pi) and print the result
There is also one method called factorial which I have made to compute the factorial in the formula
RESULTS PRODUCED
On putting limit = 1 I get -
3.14159265383525216155408817653305805189369961082170142700979067898169315680169553
On putting limit = 10 I get -
3.141592653835251522017825683538367933441136808152187507749300560259859937277166891211043856677465587830817787411902252466648641215763228301502439410432665788310356378854402610761037687401200267944051297889143892295707008454487636735727641182639754208990826247667192290712119662965838581189023690921554268043065123435639930191440467906529034361442072184293368529887693375871414064191030918467799966026346716917502424050190813
Whereas, the real value of pi is
3.1415926535 8979323846 2643383279 5028841971 6939937510 5820974944 5923078164 0628620899 8628034825 3421170679 8214808651 3282306647 0938446095......
MY QUESTION
You see, after 9 digits the result obtained by my code starts differing with the real value. Why does this happen????
Is it due to the formula or due to my code???
Please help me.
I have also read that the precision increases with increase in the limit of summation. I tried using a larger limit, still its coming out wrong.
And thank you in advance

Calculating sin function with JAVA BigDecimal -monomial is going bigger(?)

I'm making sin function with BigDecimal in JAVA, and this is as far as I go:
package taylorSeries;
import java.math.BigDecimal;
public class Sin {
private static final int cutOff = 20;
public static void main(String[] args) {
System.out.println(getSin(new BigDecimal(3.14159265358979323846264), 100));
}
public static BigDecimal getSin(BigDecimal x, int scale) {
BigDecimal sign = new BigDecimal("-1");
BigDecimal divisor = BigDecimal.ONE;
BigDecimal i = BigDecimal.ONE;
BigDecimal num = null;
BigDecimal result = x;
//System.err.println(x);
do {
x = x.abs().multiply(x.abs()).multiply(x).multiply(sign);
i = i.add(BigDecimal.ONE);
divisor = divisor.multiply(i);
i = i.add(BigDecimal.ONE);
divisor = divisor.multiply(i);
num = x.divide(divisor, scale + cutOff, BigDecimal.ROUND_HALF_UP);
result = result.add(num);
//System.out.println("d : " + divisor);
//System.out.println(divisor.compareTo(x.abs()));
System.out.println(num.setScale(9, BigDecimal.ROUND_HALF_UP));
} while(num.abs().compareTo(new BigDecimal("0.1").pow(scale + cutOff)) > 0);
System.err.println(num);
System.err.println(new BigDecimal("0.1").pow(scale + cutOff));
return result.setScale(scale, BigDecimal.ROUND_HALF_UP);
}
}
It uses Taylor series :
picture of the fomular
The monomial x is added every iteration and always negative number.
And the problem is, absolute value of x is getting bigger and bigger, so iteration never ends.
Is there and way to find them or better way to implement it from the first place?
EDIT:
I made this code from scratch with simple interest about trigonometric functions, and now I see lots of childish mistakes.
My intention first was like this:
num is x^(2k+1) / (2k+1)!
divisor is (2k+1)!
i is 2k+1
dividend is x^(2k+1)
So I update divisor and dividend with i and compute num by sign * dividend / divisor and add it to result by result = result.add(num)
so new and good-working code is:
package taylorSeries;
import java.math.BigDecimal;
import java.math.MathContext;
public class Sin {
private static final int cutOff = 20;
private static final BigDecimal PI = Pi.getPi(100);
public static void main(String[] args) {
System.out.println(getSin(Pi.getPi(100).multiply(new BigDecimal("1.5")), 100)); // Should be -1
}
public static BigDecimal getSin(final BigDecimal x, int scale) {
if (x.compareTo(PI.multiply(new BigDecimal(2))) > 0) return getSin(x.remainder(PI.multiply(new BigDecimal(2)), new MathContext(x.precision())), scale);
if (x.compareTo(PI) > 0) return getSin(x.subtract(PI), scale).multiply(new BigDecimal("-1"));
if (x.compareTo(PI.divide(new BigDecimal(2))) > 0) return getSin(PI.subtract(x), scale);
BigDecimal sign = new BigDecimal("-1");
BigDecimal divisor = BigDecimal.ONE;
BigDecimal i = BigDecimal.ONE;
BigDecimal num = null;
BigDecimal dividend = x;
BigDecimal result = dividend;
do {
dividend = dividend.multiply(x).multiply(x).multiply(sign);
i = i.add(BigDecimal.ONE);
divisor = divisor.multiply(i);
i = i.add(BigDecimal.ONE);
divisor = divisor.multiply(i);
num = dividend.divide(divisor, scale + cutOff, BigDecimal.ROUND_HALF_UP);
result = result.add(num);
} while(num.abs().compareTo(new BigDecimal("0.1").pow(scale + cutOff)) > 0);
return result.setScale(scale, BigDecimal.ROUND_HALF_UP);
}
}
The new BigDecimal(double) constructor is not something you generally want to be using; the whole reason BigDecimal exists in the first place is that double is wonky: There are almost 2^64 unique values that a double can represent, but that's it - (almost) 2^64 distinct values, smeared out logarithmically, with about a quarter of all available numbers between 0 and 1, a quarter from 1 to infinity, and the other half the same but as negative numbers. 3.14159265358979323846264 is not one of the blessed numbers. Use the string constructor instead - just toss " symbols around it.
every loop, sign should switch, well, sign. You're not doing that.
In the first loop, you overwrite x with x = x.abs().multiply(x.abs()).multiply(x).multiply(sign);, so now the 'x' value is actually -x^3, and the original x value is gone. Next loop, you repeat this process, and thus you definitely are nowhere near the desired effect. The solution - don't overwrite x. You need x, throughout the calculation. Make it final (getSin(final BigDecimal x) to help yourself.
Make another BigDecimal value and call it accumulator or what not. It starts out as a copy of x.
Every loop, you multiply x to it twice then toggle the sign. That way, the first time in the loop the accumulator is -x^3. The second time, it is x^5. The third time it is -x^7, and so on.
There is more wrong, but at some point I'm just feeding you your homework on a golden spoon.
I strongly suggest you learn to debug. Debugging is simple! All you really do, is follow along with the computer. You calculate by hand and double check that what you get (be it the result of an expression, or whether a while loop loops or not), matches what the computer gets. Check by using a debugger, or if you don't know how to do that, learn, and if you don't want to, add a ton of System.out.println statements as debugging aids. There where your expectations mismatch what the computer is doing? You found a bug. Probably one of many.
Then consider splicing parts of your code up so you can more easily check the computer's work.
For example, here, num is supposed to reflect:
before first loop: x
first loop: x - x^3/3!
second loop: x - x^3/3! + x^5/5!
etcetera. But for debugging it'd be so much simpler if you have those parts separated out. You optimally want:
first loop: 3 separated concepts: -1, x^3, and 3!.
second loop: +1, x^5, and 5!.
That debugs so much simpler.
It also leads to cleaner code, generally, so I suggest you make these separate concepts as variables, describe them, write a loop and test that they are doing what you want (e.g. you use sysouts or a debugger to actually observe the power accumulator value hopping from x to x^3 to x^5 - this is easily checked), and finally put it all together.
This is a much better way to write code than to just 'write it all, run it, realize it doesn't work, shrug, raise an eyebrow, head over to stack overflow, and pray someone's crystal ball is having a good day and they see my question'.
The fact that the terms are all negative is not the problem (though you must make it alternate to get the correct series).
The term magnitude is x^(2k+1) / (2k+1)!. The numerator is indeed growing, but so is the denominator, and past k = x, the denominator starts to "win" and the series always converges.
Anyway, you should limit yourself to small xs, otherwise the computation will be extremely lengthy, with very large products.
For the computation of the sine, always begin by reducing the argument to the range [0,π]. Even better, if you jointly develop a cosine function, you can reduce to [0,π/2].

while loop and double acting weirdly

Trying to solve a differential equation numerically (for the first time). My program asks for a bunch of parameters and then calculates the amount of time required to chill a bottle of wine to a given temperature. This is my main:
import java.util.Scanner;
public class Step2_lab11 {
public static int TAO = 50;
public static double DELTA_MINUTES = 0.1;
public static void main(String[] args) {
System.out.println("VINETS AVKYLNINGSTID \n");
System.out.println("Ange vinets temperatur:");
Scanner userIn = new Scanner(System.in);
double wineTemp = userIn.nextDouble();
System.out.println("Vinets önskade temperatur:");
double preferredTemp = userIn.nextDouble();
System.out.println("Kylens/frysens temperatur:");
double chillTemp = userIn.nextDouble();
WineChiller wineChiller = new WineChiller();
double elapsedTime = 0.0;
while(wineTemp > preferredTemp) {
elapsedTime = elapsedTime + DELTA_MINUTES;
double dT = wineChiller.getChillingTime(TAO, DELTA_MINUTES, chillTemp, preferredTemp, wineTemp);
wineTemp = wineTemp - dT;
System.out.println(elapsedTime);
}
}
}
And this is the WineChiller.java file:
public class WineChiller {
public WineChiller() {
}
public double getChillingTime(int TAO, double DELTA_MINUTES, double chillTemp, double preferredTemp, double wineTemp) {
double dT = (wineTemp - chillTemp) * DELTA_MINUTES / TAO;
return dT;
}
}
The Syso part of the while loop produces this (with wineTemp = 25, preferredTemp = 16, chillTemp = 5)
0.1
0.2
0.30000000000000004
....
29.300000000000146
29.400000000000148
29.50000000000015
29.60000000000015
29.700000000000152
29.800000000000153
29.900000000000155
No idea why it adds the random decimals. I also think (but I'm not 100%) the right answer should be exactly 30 minutes, not 29.9. Am I missing some obvious logic error here?
You need to read this.
This is just how binary numbers and IEEE floating point representation work.
You can no more represent 0.1 exactly in binary than you can 1/3 in decimal.
This is why you should not compare values when you use double or float; you need a tolerance for absolute value of differences.
Looks like a simple Euler integration of the first order ODE for transient heat transfer for a lumped mass. Make sure that you understand how time step choice affects stability and accuracy.

Truncating Double to 2 decimal places?

I know this question is repeated alot, but please have a look at the statement first than mark it already answered :)
For truncating the double values 2 decimal places I use two ways which are mostly mentioned everywhere. They are given below
//this one
DecimalFormat dtime = new DecimalFormat("#.##");
return Double.valueOf(dtime.format(val));
//or the one below
BigDecimal bd = new BigDecimal(val);
BigDecimal rounded = bd.setScale(2, BigDecimal.ROUND_HALF_UP);
return rounded.doubleValue();
The problem is that for both the ways I mostly get correct rounded values in the dataset. But strangely at the same time I get values like 2.00000000000005 or 19.97999999999.
The problem that I dont get is that why only a few values are not rounded of. What could be wrong?
For truncating the double values 2 decimal places I use two ways which are mostly mentioned everywhere.
And they are both wrong, because they are attempting the impossible. There is no such thing as truncating a double to 2 decimal places, because doubles don't have decimal places. They have binary places. See my answer here for proof. If you want decimal places you have to use a decimal radix, i.e. BigDecimal or DecimalFormat.
Issue is that floating point numbers are inherently approximate in nature, given the underlying representation. Therefore you will want to use them in places where approximations are good, and avoid them where approximations are no good (e.g. financials).
The call to rounded.doubleValue() still returns a floating point number and so it is still impacted by the limitations of the representation.
See
http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
for more information.
The following piece of code helped me in restricting the number of decimal places (truncating) for a double value.
public static Double truncate (double valueToTruncate, int numberOfDecimalPlaces) {
if (valueToTruncate > 0) {
return new BigDecimal(String.valueOf(valueToTruncate)).setScale(numberOfDecimalPlaces, BigDecimal.ROUND_FLOOR).doubleValue();
} else {
return new BigDecimal(String.valueOf(valueToTruncate)).setScale(numberOfDecimalPlaces, BigDecimal.ROUND_CEILING).doubleValue();
}
}
Hope this helps someone :)
I'm new to this but keeping everything in front of me I did it this way. Now mind you this is a truncation mathematically, I don't convert to string except in my debugs after each line.
It isn't elegant but seems to work. This is purely a problem solving framework.
Anyway;
import java.util.Scanner;
public class TestConstructs
{
private static double w;
private static int w1;
private static double w2;
private static double w3;
private static int w4;
private static double w5;
private static double w6;
public static void main(String[] args)
{
// TODO Auto-generated method stub
TestConstructs foo = new TestConstructs();
foo.setWage(w);
}
public void setWage(double w)
{
Scanner input = new Scanner(System.in);
System.out.println("Enter Wage: "); //enter something longish like 30987.978654 or w/e
w = input.nextDouble();
w1 = (int)w;
System.out.printf("%d w1\n",w1);
w2 = w - w1;
System.out.printf("%.3f w2\n",w2);
w3 = w2*100;
System.out.printf("%.3f w3\n",w3);
w4 = (int)w3;
System.out.printf("%d w4\n",w4);
w5 = (double)w4;
System.out.printf("%.3f w5\n",w5);
w6 = 0 + w5/100;
System.out.printf("%.3f w6\n",w6);
w = w1 + w6;
System.out.printf("%.3f final value\n",w); //.3 to show zero
input.close();
}
}
What I got at the end
Enter Wage:
30987.978654
30987 w1
0.979 w2
97.865 w3
97 w4
97.000 w5
0.970 w6
30987.970 final value

round up to 2 decimal places in java? [duplicate]

This question already has answers here:
How to round a number to n decimal places in Java
(39 answers)
Closed 7 years ago.
I have read a lot of stackoverflow questions but none seems to be working for me. i am using math.round() to round off.
this is the code:
class round{
public static void main(String args[]){
double a = 123.13698;
double roundOff = Math.round(a*100)/100;
System.out.println(roundOff);
}
}
the output i get is: 123 but i want it to be 123.14. i read that adding *100/100 will help but as you can see i didn't manage to get it to work.
it is absolutely essential for both input and output to be a double.
it would be great great help if you change the line 4 of the code above and post it.
Well this one works...
double roundOff = Math.round(a * 100.0) / 100.0;
Output is
123.14
Or as #Rufein said
double roundOff = (double) Math.round(a * 100) / 100;
this will do it for you as well.
double d = 2.34568;
DecimalFormat f = new DecimalFormat("##.00");
System.out.println(f.format(d));
String roundOffTo2DecPlaces(float val)
{
return String.format("%.2f", val);
}
BigDecimal a = new BigDecimal("123.13698");
BigDecimal roundOff = a.setScale(2, BigDecimal.ROUND_HALF_EVEN);
System.out.println(roundOff);
Go back to your code, and replace 100 by 100.00 and let me know if it works.
However, if you want to be formal, try this:
import java.text.DecimalFormat;
DecimalFormat df=new DecimalFormat("0.00");
String formate = df.format(value);
double finalValue = (Double)df.parse(formate) ;
double roundOff = Math.round(a*100)/100;
should be
double roundOff = Math.round(a*100)/100D;
Adding 'D' to 100 makes it Double literal, thus result produced will have precision
I know this is 2 year old question but as every body faces a problem to round off the values at some point of time.I would like to share a different way which can give us rounded values to any scale by using BigDecimal class .Here we can avoid extra steps which are required to get the final value if we use DecimalFormat("0.00") or using Math.round(a * 100) / 100 .
import java.math.BigDecimal;
public class RoundingNumbers {
public static void main(String args[]){
double number = 123.13698;
int decimalsToConsider = 2;
BigDecimal bigDecimal = new BigDecimal(number);
BigDecimal roundedWithScale = bigDecimal.setScale(2, BigDecimal.ROUND_HALF_UP);
System.out.println("Rounded value with setting scale = "+roundedWithScale);
bigDecimal = new BigDecimal(number);
BigDecimal roundedValueWithDivideLogic = bigDecimal.divide(BigDecimal.ONE,decimalsToConsider,BigDecimal.ROUND_HALF_UP);
System.out.println("Rounded value with Dividing by one = "+roundedValueWithDivideLogic);
}
}
This program would give us below output
Rounded value with setting scale = 123.14
Rounded value with Dividing by one = 123.14
Try :
class round{
public static void main(String args[]){
double a = 123.13698;
double roundOff = Math.round(a*100)/100;
String.format("%.3f", roundOff); //%.3f defines decimal precision you want
System.out.println(roundOff); }}
This is long one but a full proof solution, never fails
Just pass your number to this function as a double, it will return you rounding the decimal value up to the nearest value of 5;
if 4.25, Output 4.25
if 4.20, Output 4.20
if 4.24, Output 4.20
if 4.26, Output 4.30
if you want to round upto 2 decimal places,then use
DecimalFormat df = new DecimalFormat("#.##");
roundToMultipleOfFive(Double.valueOf(df.format(number)));
if up to 3 places, new DecimalFormat("#.###")
if up to n places, new DecimalFormat("#.nTimes #")
public double roundToMultipleOfFive(double x)
{
x=input.nextDouble();
String str=String.valueOf(x);
int pos=0;
for(int i=0;i<str.length();i++)
{
if(str.charAt(i)=='.')
{
pos=i;
break;
}
}
int after=Integer.parseInt(str.substring(pos+1,str.length()));
int Q=after/5;
int R =after%5;
if((Q%2)==0)
{
after=after-R;
}
else
{
if(5-R==5)
{
after=after;
}
else after=after+(5-R);
}
return Double.parseDouble(str.substring(0,pos+1).concat(String.valueOf(after))));
}
seems like you are hit by integer arithmetic: in some languages (int)/(int) will always be evaluated as integer arithmetic.
in order to force floating-point arithmetic, make sure that at least one of the operands is non-integer:
double roundOff = Math.round(a*100)/100.f;
I just modified your code. It works fine in my system. See if this helps
class round{
public static void main(String args[]){
double a = 123.13698;
double roundOff = Math.round(a*100)/100.00;
System.out.println(roundOff);
}
}
public static float roundFloat(float in) {
return ((int)((in*100f)+0.5f))/100f;
}
Should be ok for most cases. You can still changes types if you want to be compliant with doubles for instance.

Categories

Resources