PI16,PI19,PI26,FFT,10000,65536,method,impleme
Quick Search for:  in language:    
PI16,PI19,PI26,FFT,10000,65536,method,impleme
   Code/Articles » |  Newest/Best » |  Community » |  Jobs » |  Other » |  Goto » | 
CategoriesSearch Newest CodeCoding ContestCode of the DayAsk A ProJobsUpload
C/ C++ Stats

 Code: 451,578 lines
 Jobs: 558 postings

 
Sponsored by:

 

You are in:

 
Login



Latest Code Ticker for C/ C++.
Url Test
By Richard Creary on 8/27


Nice Console Calc
By Andrew Carter on 8/26


Visual Pi Hex 2
By jo122321323 on 8/26


Cascade Clone v1.0
By Paulo Jorente - aka JungleBoy on 8/26


Bubble Sort Algo
By d1rtyw0rm on 8/26


Copy File I/O
By Olivier Bastien on 8/25


Visual Pi Hex
By jo122321323 on 8/25


Click here to see a screenshot of this code!ShortCutSample
By Massimiliano Tomassetti on 8/25

(Screen Shot)

AnPMoneyManager beta
By Anthony Tristan on 8/24


Click here to put this ticker on your site!


Add this ticker to your desktop!


Daily Code Email
To join the 'Code of the Day' Mailing List click here!





Affiliate Sites



 
 
   

Calculate PI to 10's of thousands of digits

Print
Email
 
VB icon
Submitted on: 7/29/2000 9:27:17 PM
By: Bob Stout (republished under Open Content License)  
Level: Intermediate
User Rating: Unrated
Compatibility:C, C++ (general), Microsoft Visual C++, Borland C++, UNIX C++

Users have accessed this code 5532 times.
 
 
     This method implements the Salamin / Brent / Gauss Arithmetic- Geometric Mean pi formula. Let A[0] = 1, B[0] = 1/Sqrt(2) Then iterate from 1 to 'n'. A[n] = (A[n-1] + B[n-1])/2 B[n] = Sqrt(A[n-1]*B[n-1]) C[n]^2 = A[n]^2 - B[n]^2 (or) C[n] = (A[n-1]-B[n-1])/2 n PI[n] = 4A[n+1]^2 / (1-(Sum (2^(j+1))*C[j]^2)) j = 1 There is an actual error calculation, but it comes out to slightly more than double on each iteration. I think it results in about 17 million correct digits, instead of 16 million if it actually doubled. PI16 generates 178,000 digits. PI19 to over a million. PI22 is 10 million, and PI26 to 200 million. For what little it's worth, this program is placed into the public domain by its author, Carey Bloodworth, on September 21, 1996. The math routines in this program are not general purpose routines. They have been optimized and written for this specific use. The concepts are of course portable, but not the implementation. The program run time is about O(n^1.7). That's fairly good growth, compared to things such as the classic arctangents. But not as good as it should be, if I used a FFT based multiplication. Also, the 'O' is fairly large. In fact, I'd guess that this program could compute one million digits of pi in about the same time as my previously posted arctangent method, in spite of this one having less than n^2 growth. The program has not been cleaned up. It's written rather crudely and dirty. The RSqrt() in particular is rather dirty, having gone through numerous changes and attempts at speeding it up. But I'm not planning on doing any more with it. The growth isn't as low as I'd hoped, and until I find a faster multiplication, the method isn't any better than simpler arctangents. I currently use a base of 10,000 simply because it made debugging easier. A base of 65,536 and modifying the FastMul() to handle sizes that aren't powers of two would allow a bit better efficiency.
 
code:
Can't Copy and Paste this?
Click here for a copy-and-paste friendly version of this code!
 
Terms of Agreement:   
By using this code, you agree to the following terms...   
1) You may use this code in your own programs (and may compile it into a program and distribute it in compiled format for languages that allow it) freely and with no charge.   
2) You MAY NOT redistribute this code (for example to a web site) without written permission from the original author. Failure to do so is a violation of copyright laws.   
3) You may link to this code from another website, but ONLY if it is not wrapped in a frame. 
4) You will abide by any additional copyright restrictions which the author may have placed in the code or code's description.

//**************************************
//     
// Name: Calculate PI to 10's of thousan
//     ds of digits
// Description:This method implements th
//     e Salamin / Brent / Gauss Arithmetic-
Geometric Mean pi formula.
Let A[0] = 1, B[0] = 1/Sqrt(2)
Then iterate from 1 to 'n'.
A[n] = (A[n-1] + B[n-1])/2
B[n] = Sqrt(A[n-1]*B[n-1])
C[n]^2 = A[n]^2 - B[n]^2 (or) C[n] = (A[n-1]-B[n-1])/2
n
PI[n] = 4A[n+1]^2 / (1-(Sum (2^(j+1))*C[j]^2))
j = 1
There is an actual error calculation, but it comes out to slightly
more than double on each iteration. I think it results in about 17
million correct digits, instead of 16 million if it actually
doubled. PI16 generates 178,000 digits. PI19 to over a million.
PI22 is 10 million, and PI26 to 200 million.
for what little it's worth, this program is placed into the public
domain by its author, Carey Bloodworth, on September 21, 1996.
The math routines in this program are not general purpose routines.
They have been optimized and written for this specific use. The
concepts are of course portable, but not the implementation.
The program run time is about O(n^1.7). That's fairly good growth,
compared to things such as the classic arctangents. But not as good
as it should be, if I used a FFT based multiplication. Also, the 'O'
is fairly large. In fact, I'd guess that this program could compute
one million digits of pi in about the same time as my previously
posted arctangent method, in spite of this one having less than n^2
growth.
The program has not been cleaned up. It's written rather crudely
and dirty. The RSqrt() in particular is rather dirty, having gone
through numerous changes and attempts at speeding it up.
But I'm not planning on doing any more with it. The growth isn't as
low as I'd hoped, and until I find a faster multiplication, the
method isn't any better than simpler arctangents.
I currently use a base of 10,000 simply because it made debugging
easier. A base of 65,536 and modifying the FastMul() to handle sizes
that aren't powers of two would allow a bit better efficiency.
// By: Bob Stout (republished under Open
//     Content License)
//
//This code is copyrighted and has// limited warranties.Please see http://
//     www.Planet-Source-Code.com/xq/ASP/txtCod
//     eId.657/lngWId.3/qx/vb/scripts/ShowCode.
//     htm//for details.//**************************************
//     

/* +++Date last modified: 05-Jul-1997 */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include "snipmath.h"
#ifdef __TURBOC__
typedef short int Indexer;
#else
typedef long int Indexer;
#endif
typedef short int Short;
typedef long int Long;
short *a, *b, *c, *Work1, *MulWork, *Work2, *Work3;
int SIZE;
/*
** Work1 is explicitly used in Reciprocal() and RSqrt()
** Work1 is implicitly used in Divide() and Sqrt()
** Work2 is explicitly used in Divide() and Sqrt()
** Work3 is used only in the AGM and holds the previous reciprocal
** square root, allowing us to save some time in RSqrt()
*/
void Copy(Short *a, Short *b, Indexer Len)
    {
    while (Len--) a[Len] = b[Len];
}

/* ** this rounds and copies a 2x mul result into a normal result ** Our number format will never have more than one unit of integer, ** and after a mul, we have two, so we need to fix that. */ void Round2x(Short *a, Short *b, Indexer Len) { Indexer x; short carry; carry = 0; if (b[Len+1] >= 5000) carry = 1; for (x = Len; x > 0; x--) { carry += b[x]; a[x-1] = carry % 10000; carry /= 10000; }
}
void DivBy2(Short *n, Indexer Len) { Indexer x; long temp; temp = 0; for (x = 0; x < Len; x++) { temp = (Long)n[x]+temp*10000; n[x] = (Short)(temp/2); temp = temp%2; }
}
void DoCarries(Short *limit, Short *cur, Short carry) { long temp; while ((cur >= limit) && (carry != 0)) { temp = *cur+carry; carry = 0; if (temp >= 10000) { carry = 1; temp -= 10000; }
*cur = temp; --cur; }
}
void DoBorrows(Short *limit, Short *cur, Short borrow) { long temp; while ((cur >= limit) && (borrow != 0)) { temp = *cur-borrow; borrow = 0; if (temp < 0) { borrow = 1; temp += 10000; } *cur = temp; --cur; };
}
void PrintShort2(char *str, Short *num, Indexer Len) { Indexer x; printf("%s ", str); printf("%u.", num[0]); for (x = 1; x < Len; x++) printf("%04u", num[x]); printf("\n"); }
void PrintShort(char *str, Short *num, Indexer Len) { Indexer x; int printed = 0; printf("%s ", str); printf("%u.\n", num[0]); for (x = 1; x < Len; x++) { printf("%02d", num[x]/100); printed += 2; if ((printed % 1000) == 0) { printf("\n\n\n\n"); printed = 0; }
else if ((printed % 50) == 0) { printf("\n"); }
else if ((printed % 10) == 0) { printf(" "); }
printf("%02d", num[x] % 100); printed += 2; if ((printed % 1000) == 0) { printf("\n\n\n\n"); printed = 0; }
else if ((printed % 50) == 0) { printf("\n"); }
else if ((printed % 10) == 0) { printf(" "); }
}
printf("\n"); }
/* sum = a + b */ short Add(Short *sum, Short *a, Short *b, Indexer Len) { long s, carry; Indexer x; carry = 0; for (x = Len - 1; x >= 0; x--) { s = (Long)a[x] + (Long)b[x] + carry; sum[x] = (Short)(s%10000); carry = s/10000; }
return carry; }
/* dif = a-b */ short Sub(Short *dif, Short *a, Short *b, Indexer Len) { long d, borrow; Indexer x; borrow = 0; for (x = Len - 1; x >= 0; x--) { d = (Long)a[x] - (Long)b[x] - borrow; borrow = 0; if (d < 0) { borrow = 1; d += 10000; }
dif[x] = (Short)d; }
return borrow; }
void Negate(Short *num, Indexer Len) { Indexer x;Long d, borrow; borrow = 0; for (x = Len - 1; x >= 0; x--) { d = 0 - num[x] - borrow; borrow = 0; if (d < 0) { borrow = 1; d += 10000; }
num[x] = (Short)d; }
}
/* prod = a*b. prod should be twice normal length */ void Mul(Short *prod, Short *a, Short *b, Indexer Len) { long p; Indexer ia, ib, ip; if ((prod == a) || (b == prod)) { printf("MUL product can't be one of the other arguments\n"); exit(1); }
for (ip = 0; ip < Len * 2; ip++) prod[ip] = 0; for (ib = Len - 1; ib >= 0; ib--) { if (b[ib] == 0) continue; ip = ib + Len; p = 0; for (ia = Len - 1; ia >= 0; ia--) { p = (Long)a[ia]*(Long)b[ib] + p + prod[ip]; prod[ip] = p%10000; p = p/10000; ip--; }
while ((p) && (ip >= 0)) { p += prod[ip]; prod[ip] = p%10000; p = p/10000; ip--; }
}
}
/* ** this is based on the simple O(n^1.585) method, although my ** growth seems to be closer to O(n^1.7) ** ** It's fairly simple. a*b is: a2b2(B^2+B)+(a2-a1)(b1-b2)B+a1b1(B+1) ** ** for a = 4711 and b = 6397, a2 = 47 a1 = 11 b2 = 63 b1 = 97 Base = 100 ** ** if we did that the normal way, we'd do ** a2b2 = 47*63 = 2961 ** a2b1 = 47*97 = 4559 ** a1b2 = 11*63 = 693 ** a1b1 = 11*97 = 1067 ** ** 29 61 **45 59 ** 6 93 **10 67 ** ----------- ** 30 13 62 67 ** ** Or, we'd need N*N multiplications. ** ** With the 'fractal' method, we compute: ** a2b2 = 47*63 = 2961 ** (a2-b1)(b1-b2) = (47-11)(97-63) = 36*34 = 1224 ** a1b1 = 11*97 = 1067 ** ** 29 61 **29 61 **12 24 **10 67 **10 67 ** ----------- ** 30 13 62 67 ** ** We need only 3 multiplications, plus a few additions. And of course, ** additions are a lot simpler and faster than multiplications, so we ** end up ahead. ** ** The way it is written requires that the size to be a power of two. ** That's why the program itself requires the size to be a power of two. ** There is no actual requirement in the method, it's just how I did it ** because I would be working close to powers of two anyway, and it was ** easier. */ void FastMul(Short *prod, Short *a, Short *b, Indexer Len) { Indexer x, HLen; int SignA, SignB; short *offset; short *NextProd; /* ** this is the threshold where it's faster to do it the ordinary way ** On my computer, it's 16. Yours may be different. */ if (Len <= 16 ) { Mul(prod, a, b, Len); return; }
HLen = Len/2; NextProd = prod + 2*Len; SignA = Sub(prod, a, a + HLen, HLen); if (SignA) { SignA = 1; Negate(prod, HLen); }
SignB = Sub(prod + HLen, b + HLen, b, HLen); if (SignB) { SignB = 1; Negate(prod + HLen, HLen); }
FastMul(NextProd, prod, prod + HLen, HLen); for (x = 0; x < 2 * Len; x++) prod[x] = 0; offset = prod + HLen; if (SignA == SignB) DoCarries(prod, offset - 1, Add(offset, offset, NextProd, Len)); else DoBorrows(prod, offset - 1, Sub(offset, offset, NextProd, Len)); FastMul(NextProd, a, b, HLen); offset = prod + HLen; DoCarries(prod, offset - 1, Add(offset, offset, NextProd, Len)); Add(prod, prod, NextProd, Len); FastMul(NextProd, a + HLen, b + HLen, HLen); offset = prod + HLen; DoCarries(prod, offset - 1, Add(offset, offset, NextProd, Len)); offset = prod + Len; DoCarries(prod, offset - 1, Add(offset, offset, NextProd, Len)); }
/* ** Compute the reciprocal of 'a'. ** x[k+1] = x[k]*(2-a*x[k]) */ void Reciprocal(Short *r, Short *n, Indexer Len) { Indexer x, SubLen; int iter; double t; /* Estimate our first reciprocal */ for (x = 0; x < Len; x++) r[x] = 0; t = n[0] + n[1]*1.0e-4 + n[2]*1.0e-8; if (t == 0.0) t = 1; t = 1.0/t; r[0] = t; t = (t - floor(t))*10000.0; r[1] = t; t = (t - floor(t))*10000.0; r[2] = t; iter = log(SIZE)/log(2.0) + 1; SubLen = 1; while (iter--) { SubLen *= 2; if (SubLen > SIZE) SubLen = SIZE; FastMul(MulWork, n, r, SubLen); Round2x(Work1, MulWork, SubLen); MulWork[0] = 2; for (x = 1; x < Len; x++) MulWork[x] = 0; Sub(Work1, MulWork, Work1, SubLen); FastMul(MulWork, r, Work1, SubLen); Round2x(r, MulWork, SubLen); }
}
/* ** Computes the reciprocal of the square root of 'a' ** y[k+1] = y[k]*(3-a*y[k]^2)/2 ** ** ** We can save quite a bit of time by using part of our previous ** results! Since the number is converging onto a specific value, ** at least part of our previous result will be correct, so we ** can skip a bit of work. */ void RSqrt(Short *r, Short *n, Indexer Len, Indexer SubLen) { Indexer x; int iter; double t; /* Estimate our first reciprocal square root */ if (SubLen < 2 ) { for (x = 0; x < Len; x++) r[x] = 0; t = n[0] + n[1]*1.0e-4 + n[2]*1.0e-8; if (t == 0.0) t = 1; t = 1.0/sqrt(t); r[0] = t; t = (t - floor(t))*10000.0; r[1] = t; t = (t - floor(t))*10000.0; r[2] = t; }
/* ** PrintShort2("\n ~R: ", r, SIZE); */ if (SubLen > SIZE) SubLen = SIZE; iter = SubLen; while (iter <= SIZE*2) { FastMul(MulWork, r, r, SubLen); Round2x(Work1, MulWork, SubLen); FastMul(MulWork, n, Work1, SubLen); Round2x(Work1, MulWork, SubLen); MulWork[0] = 3; for (x = 1; x < SubLen; x++) MulWork[x] = 0; Sub(Work1, MulWork, Work1, SubLen); FastMul(MulWork, r, Work1, SubLen); Round2x(r, MulWork, SubLen); DivBy2(r, SubLen); /* printf("%3d", SubLen); PrintShort2("R: ", r, SubLen); printf("%3d", SubLen); PrintShort2("R: ", r, Len); */ SubLen *= 2; if (SubLen > SIZE) SubLen = SIZE; iter *= 2; }
}
/* ** d = a/b by computing the reciprocal of b and then multiplying ** that by a. It's faster this way because the normal digit by ** digit method has N^2 growth, and this method will have the same ** growth as our multiplication method. */ void Divide(Short *d, Short *a, Short *b, Indexer Len) { Reciprocal(Work2, b, Len); FastMul(MulWork, a, Work2, Len); Round2x(d, MulWork, Len); }
/* ** r = sqrt(n) by computing the reciprocal of the square root of ** n and then multiplying that by n */ void Sqrt(Short *r, Short *n, Indexer Len, Indexer SubLen) { RSqrt(Work2, n, Len, SubLen); FastMul(MulWork, n, Work2, Len); Round2x(r, MulWork, Len); }
void usage(void) { puts("This program requires the number of digits/4 to calculate"); puts("This number must be a power of two."); exit(-1); }
int main(int argc,char *argv[]) { Indexer x; Indexer SubLen; double Pow2, tempd, carryd; int AGMIter; int NeededIter; clock_t T0, T1, T2, T3; if (argc < 2) usage(); SIZE = atoi(argv[1]); if (!ispow2(SIZE)) usage(); a = (Short *)malloc(sizeof(Short)*SIZE); b = (Short *)malloc(sizeof(Short)*SIZE); c = (Short *)malloc(sizeof(Short)*SIZE); Work1 = (Short *)malloc(sizeof(Short)*SIZE); Work2 = (Short *)malloc(sizeof(Short)*SIZE); Work3 = (Short *)malloc(sizeof(Short)*SIZE); MulWork = (Short *)malloc(sizeof(Short)*SIZE*4); if ((a ==NULL) || (b == NULL) || (c == NULL) || (Work1 == NULL) || (Work2 == NULL) || (MulWork == NULL)) { printf("Unable to allocate memory\n");exit(1); }
NeededIter = log(SIZE)/log(2.0) + 1; Pow2 = 4.0; AGMIter = NeededIter + 1; SubLen = 1; T0 = clock(); for (x = 0; x < SIZE; x++) a[x] = b[x] = c[x] = Work3[x] = 0; a[0] = 1; Work3[0] = 2; RSqrt(b, Work3, SIZE, 1); c[0] = 1; T1 = clock(); /* printf("AGMIter %d\n", AGMIter); PrintShort2("a AGM: ", a, SIZE); PrintShort2("b AGM: ", b, SIZE); PrintShort2("C sum: ", c, SIZE); */ while (AGMIter--) { Sub(Work1, a, b, SIZE);/* w = (a-b)/2 */ DivBy2(Work1, SIZE); FastMul(MulWork, Work1, Work1, SIZE); /* m = w*w */ Round2x(Work1, MulWork, SIZE); carryd = 0.0; /* m = m* w^(J+1)*/ for (x = SIZE - 1; x >= 0; x--) { tempd = Pow2*Work1[x] + carryd; carryd = floor(tempd / 10000.0); Work1[x] = tempd - carryd*10000.0; }
Pow2 *= 2.0; Sub(c, c, Work1, SIZE);/* c = c - m*/ /* Save some time on last iter */ if (AGMIter != 0) FastMul(MulWork, a, b, SIZE);/* m = a*b */ Add(a, a, b, SIZE);/* a = (a+b)/2 */ DivBy2(a, SIZE); if (AGMIter != 0) { Round2x(Work3, MulWork, SIZE); Sqrt(b, Work3, SIZE, SubLen); /* b = sqrt(a*b) */ }
/* printf("AGMIter %d\n", AGMIter); PrintShort2("a AGM: ", a, SIZE); PrintShort2("b AGM: ", b, SIZE); PrintShort2("C sum: ", c, SIZE); */ SubLen *= 2;if (SubLen > SIZE) SubLen = SIZE; }
T2 = clock(); FastMul(MulWork, a, a, SIZE); Round2x(a, MulWork, SIZE); carryd = 0.0; for (x = SIZE - 1; x >= 0; x--) { tempd = 4.0*a[x] + carryd; carryd = floor(tempd / 10000.0); a[x] = tempd - carryd*10000.0; }
Divide(b, a, c, SIZE); T3 = clock(); PrintShort("\nPI = ", b, SIZE); printf("\nTotal Execution time: %12.4lf\n", (double)(T3 - T0) / CLOCKS_PER_SEC); printf("Setup time : %12.4lf\n", (double)(T1 - T0) / CLOCKS_PER_SEC); printf("AGM Calculation time: %12.4lf\n", (double)(T2 - T1) / CLOCKS_PER_SEC); printf("Post calc time : %12.4lf\n", (double)(T3 - T2) / CLOCKS_PER_SEC); return 0; }


Other 155 submission(s) by this author

 

 
Report Bad Submission
Use this form to notify us if this entry should be deleted (i.e contains no code, is a virus, etc.).
Reason:
 
Your Vote!

What do you think of this code(in the Intermediate category)?
(The code with your highest vote will win this month's coding contest!)
Excellent  Good  Average  Below Average  Poor See Voting Log
 
Other User Comments
7/31/2000 1:27:51 PM:Gideon
Where is "snipmath.h" ?
Keep the Planet clean! If this comment was disrespectful, please report it:
Reason:

 
8/9/2000 8:32:56 PM:Oskilian
What exactly is snipmath.h, it´s not in 
vc6 nor in borland builder, or in turbo 
c++ include folders
Keep the Planet clean! If this comment was disrespectful, please report it:
Reason:

 
8/26/2000 10:37:18 PM:Josh
I think all these people who submit 
code should
pay attention to what 
files they are including.
This is the 
sixth piece of code I've seen 
that
they don't bother to include the 
extra files
needed to run the program. 
The problem in this
app is that there 
is no "snipmath.h" included with
it. 
This goes as a lesson for all the rest 
of the
code writers out there. Some of 
the code looks
extremelu useful, but 
there's almost always a missing
header 
file.
Keep the Planet clean! If this comment was disrespectful, please report it:
Reason:

 
10/12/2000 6:01:31 PM:tony neilsen
and when you've got your 60 zillion pi 
digits, what are you going to do with 
them?
Keep the Planet clean! If this comment was disrespectful, please report it:
Reason:

 
8/19/2001 3:00:18 AM:Sean
If you are looking for the headers you 
actually need 4 different headers. They 
are: pi.h  ,  round.h  ,  snipmath.h  , 
 sniptype.h  If you wonder "Well where 
do I get these!?" well goto this 
address.  
http://www.geocities.com/SiliconValley/B
ay/1055/snippets1.htm  all it took was 
two seconds to find.
Keep the Planet clean! If this comment was disrespectful, please report it:
Reason:

 
10/8/2001 7:16:53 PM:storm391@hotmail.com
Hey,
I got all of the header files, 
but when I try to compile the code, I 
get an error 
Keep the Planet clean! If this comment was disrespectful, please report it:
Reason:

 
10/8/2001 7:17:17 PM:storm391@hotmail.com
Hey,
I got all of the header files, 
but when I try to compile the code, I 
get an error "illegal name overload", 
on the line "int SIZE;".  Im using 
CodeWarrior 6.  Any ideas?  Please 
email me.
Thanks.
Keep the Planet clean! If this comment was disrespectful, please report it:
Reason:

 
10/22/2001 4:47:30 PM:Randy
i got the 4 files and i got 1 error and 
14 warnings at compile.
Keep the Planet clean! If this comment was disrespectful, please report it:
Reason:

 
Add Your Feedback!
Note:Not only will your feedback be posted, but an email will be sent to the code's author in your name.

NOTICE: The author of this code has been kind enough to share it with you.  If you have a criticism, please state it politely or it will be deleted.

For feedback not related to this particular code, please click here.
 
Name:
Comment:

 

Categories | Articles and Tutorials | Advanced Search | Recommended Reading | Upload | Newest Code | Code of the Month | Code of the Day | All Time Hall of Fame | Coding Contest | Search for a job | Post a Job | Ask a Pro Discussion Forum | Live Chat | Feedback | Customize | C/ C++ Home | Site Home | Other Sites | About the Site | Feedback | Link to the Site | Awards | Advertising | Privacy

Copyright© 1997 by Exhedra Solutions, Inc. All Rights Reserved.  By using this site you agree to its Terms and Conditions.  Planet Source Code (tm) and the phrase "Dream It. Code It" (tm) are trademarks of Exhedra Solutions, Inc.