program piqpr16 ! This program implements the BBP algorithm to generate a few hexadecimal ! digits beginning immediately after a given position id, or in other words ! beginning at position id + 1. The maximum value of id is not known for ! this code, but it is believed to be at least 10^14, depending on system. ! Results for a given position id can be checked by repeating with id-1 or ! id+1, and verifying that the hex digits perfectly overlap with an offset of ! one, except possibly for a few trailing digits. The resulting fractions ! are typically accurate to at least 25 decimal digits, and to at least 20 ! hex digits. This code runs reasonably fast on IBM systems and others that ! treat "real*16" as double-double, but typically rather slowly on systems ! that treat "real*16" as software-based IEEE 128-bit floating-point. ! The function subprogram "second" below is a stub that may be replaced with ! a CPU time function. ! David H. Bailey 2006-09-06 implicit none integer id, nbt real*8 second, tm0, tm1 real*16 c1, pid, s1, s2, s3, s4, series parameter (nbt = 96) integer ibt(nbt) character*1 chx(nbt/4) ! Set the position id here. Output hex digits beginning after position id, ! i.e., beginning with position id + 1. parameter (id = 10000000) write (6, 1) id 1 format ('Pi hex digit computation'/'position =',i12) ! Evaluate the four series used in the formula. tm0 = second () s1 = series (1, id) call ibits (s1, nbt, ibt, chx) write (6, 2) 1, s1, chx, ibt 2 format ('Sum of series',i2,' ='/f34.30/24a1/96i1.1) s2 = series (4, id) call ibits (s2, nbt, ibt, chx) write (6, 2) 2, s2, chx, ibt s3 = series (5, id) call ibits (s3, nbt, ibt, chx) write (6, 2) 3, s3, chx, ibt s4 = series (6, id) call ibits (s4, nbt, ibt, chx) write (6, 2) 4, s4, chx, ibt ! Compute the four series results as specified in the BBP formula. c1 = 1.d0 pid = mod (4.d0 * s1 - 2.d0 * s2 - s3 - s4, c1) if (pid .lt. 0.d0) pid = pid + 1.d0 tm1 = second () ! Output results. write (6, 3) tm1 - tm0 3 format ('Run complete. CPU time =',f12.4) call ibits (pid, nbt, ibt, chx) write (6, 4) pid, chx, ibt 4 format ('Pi fraction ='/f34.30/24a1/96i1.1) stop end function second () real*8 second second = 0.d0 return end subroutine ibits (x, nbt, ibt, chx) ! This returns in ibt the first nbt bits of the fraction of the floating-point ! argument x, and returns the first nbt/4 hex digits in chx. The argument nbt ! must be divisible by 4 and must not exceed the number of mantissa bits in ! the floating-point format used for x. implicit none integer i, ii, nbt, ibt(nbt) real*16 x, y character*1 chx(nbt/4), hx(0:15) data hx/'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', & 'C', 'D', 'E', 'F'/ y = abs (x) do i = 1, nbt y = 2.d0 * (y - aint (y)) ibt(i) = y enddo do i = 1, nbt / 4 ii = 8 * ibt(4*i-3) + 4 * ibt(4*i-2) + 2 * ibt(4*i-1) + ibt(4*i) chx(i) = hx(ii) enddo return end function series (m, id) ! This routine evaluates the series Sum_{k=1}^d (2^(id-k) mod (8*k+m))/(8*k+m). implicit none integer id, j, k, m real*16 ak, c1, eps, expm1, expm2, p, s, series, t parameter (eps = 1d-32) c1 = 1.d0 s = 0.d0 ! Sum the series until k exceeds id do k = 0, id ak = 8 * k + m p = id - k if (p == 0.d0) then t = 1.d0 elseif (ak == 1.d0) then t = 0.d0 else t = expm1 (p, ak) endif s = mod (s + t / ak, c1) ! write (6, '(i5,3f15.1,f20.15)') k, p, ak, t, s enddo ! Complete the computation by manually computing a few terms where k > id. do k = id + 1, id + 100 ak = 8 * k + m t = 16.d0 ** (id - k) if (t .lt. eps) goto 120 s = mod (s + t / ak, c1) enddo 120 continue ! write (6, *) 'final s =', s series = s return end function expm1 (p, ak) ! expm1 (p, ak) = 16^p mod ak. ! This routine uses a left-to-right binary exponentiation scheme. implicit none integer i, ntp parameter (ntp = 30) real*16 ak, expm1, p, p1, pt, pt1, r, tp(ntp) save tp data tp/ntp*0.d0/ ! If this is the first call, fill the power of two table tp. if (tp(1) .eq. 0.d0) then tp(1) = 1.d0 do i = 2, ntp tp(i) = 2.d0 * tp(i-1) enddo endif ! Find the largest power of two less than or equal to p. do i = 1, ntp if (tp(i) .gt. p) goto 100 enddo 100 continue pt1 = tp(i-1) p1 = p r = 1.d0 ! Perform the binary exponentiation scheme. 110 continue if (p1 .ge. pt1) then r = mod (16.d0 * r, ak) p1 = p1 - pt1 endif pt1 = 0.5d0 * pt1 if (pt1 .ge. 1.d0) then r = mod (r * r, ak) goto 110 endif expm1 = mod (r, ak) return end