it's about 3+ times faster than the Brent-Salamin algorithm.
Code: Select all
/'
python3 program to calculate pi using python long integers, binary
splitting and the chudnovsky algorithm
see: https://www.craig-wood.com/nick/articles/pi-chudnovsky/ for more info
nick craig-wood <nick@craig-wood.com>
'/
#Include Once "gmp.bi"
Dim Shared As __mpz_struct temp, c3_over_24
mpz_init(@temp)
mpz_init(@c3_over_24)
mpz_set_ui(@c3_over_24, 640320)
mpz_mul(@temp, @c3_over_24, @c3_over_24)
mpz_mul(@c3_over_24, @temp, @c3_over_24)
mpz_tdiv_q_ui(@c3_over_24,@c3_over_24,24)
Sub bs(Byval a As Uinteger, Byval b As Uinteger, Byref pab As __mpz_struct, Byref qab As __mpz_struct, Byref tab_ As __mpz_struct)
/'
computes the terms for binary splitting the chudnovsky infinite series
a(a) = +/- (13591409 + 545140134*a)
p(a) = (6*a-5)*(2*a-1)*(6*a-1)
b(a) = 1
q(a) = a*a*a*c3_over_24
returns p(a,b), q(a,b) and t(a,b) '/
Dim As Uinteger m, t
If b - a = 1 Then
' directly compute p(a,a+1), q(a,a+1) and t(a,a+1)
If a = 0 Then
mpz_set_ui(@pab, 1)
mpz_set_ui(@qab, 1)
Else
t=6*a
mpz_set_ui(@pab, t-1)
mpz_mul_ui(@pab,@pab,a+a-1)
mpz_mul_ui(@pab,@pab,t-5)
mpz_set_ui(@temp,a)
mpz_mul(@qab,@temp,@temp)
mpz_mul(@qab,@qab,@temp)
mpz_mul(@qab,@qab,@c3_over_24)
End If
mpz_set_ui(@tab_,545140134)
mpz_mul_ui(@tab_,@tab_,a)
mpz_add_ui(@tab_,@tab_,13591409)
mpz_mul(@tab_,@tab_,@pab) ' a(a) * p(a)
If a And 1 Then
mpz_neg(@tab_,@tab_)' = -tab
End If
Else
Dim As __mpz_struct pam, qam, tam, pmb, qmb, tmb
mpz_init(@pam)
mpz_init(@qam)
mpz_init(@tam)
mpz_init(@pmb)
mpz_init(@qmb)
mpz_init(@tmb)
' recursively compute p(a,b), q(a,b) and t(a,b)
' m is the midpoint of a and b
m = (a + b) / 2
' recursively calculate p(a,m), q(a,m) and t(a,m)
bs(a, m, pam, qam, tam)
' recursively calculate p(m,b), q(m,b) and t(m,b)
bs(m, b, pmb, qmb, tmb)
' now combine
mpz_mul(@pab, @pam, @pmb)
mpz_mul(@qab, @qam, @qmb)
mpz_mul(@temp, @qmb, @tam)
mpz_mul(@tab_, @pam, @tmb)
mpz_add(@tab_, @tab_, @temp)
mpz_clear(@tmb)
mpz_clear(@qmb)
mpz_clear(@pmb)
mpz_clear(@tam)
mpz_clear(@qam)
mpz_clear(@pam)
End If
End Sub
Sub pi_chudnovsky_bs(Byref pi As __mpz_struct, Byval digits As Uinteger)
/'
compute int(pi * 10**digits)
this is done using chudnovsky's series with binary splitting
'/
Dim As __mpz_struct one_squared, p, q, sqrtc, t
Dim As Uinteger n
Dim As Double digits_per_term
mpz_init(@one_squared)
mpz_init(@p)
mpz_init(@q)
mpz_init(@sqrtc)
mpz_init(@t)
' mpz_set_ui (@c, 640320)
' mpz_mul (@c3_over_24, @c, @c)
' mpz_mul (@c3_over_24, @c3_over_24, @c)
' mpz_cdiv_q_ui (@c3_over_24, @c3_over_24, 24)
' how many terms to compute
digits_per_term = Log(mpz_get_d (@c3_over_24)/6/2/6)*0.4342944819032518
n = Int(digits/digits_per_term + 1)
' calclate p(0,n) and q(0,n)
bs(0, n, p, q, t)
mpz_set_ui(@one_squared,10)
mpz_pow_ui(@one_squared,@one_squared,2*digits)
mpz_mul_ui(@sqrtc,@one_squared,10005)
mpz_sqrt(@sqrtc,@sqrtc)
mpz_mul_ui(@pi,@sqrtc,426880)
mpz_mul(@pi,@pi,@q)
mpz_tdiv_q(@pi,@pi,@t)
mpz_clear(@t)
mpz_clear(@sqrtc)
mpz_clear(@q)
mpz_clear(@p)
mpz_clear(@one_squared)
End Sub
Dim As Double t1, t2
Dim As __mpz_struct pi
mpz_init(@pi)
t1=Timer
pi_chudnovsky_bs(pi,1000000)
t2=Timer
gmp_printf(!"%Zd\n",@pi)
Print "elapsed time: ";t2-t1
Print "press RETURN to end ";
Sleep
mpz_clear(@pi)
mpz_clear(@c3_over_24)
mpz_clear(@temp)