ex-2/fanc{y,ier}: implement proper computation of N
This commit is contained in:
parent
845548fa93
commit
dd7a3055ee
@ -2,27 +2,42 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <math.h>
|
||||
#include <gsl/gsl_sf_lambert.h>
|
||||
#include <gmp.h>
|
||||
#include <time.h>
|
||||
|
||||
/* The Euler-Mascheroni constant is here computed to
|
||||
* arbitrary precision using GMP rational numbers
|
||||
* with the formula:
|
||||
* and the standard Brent-McMillan algorithm.
|
||||
*
|
||||
* γ = A(N)/B(N) - C(N)/B(N)² - log(N)
|
||||
*
|
||||
* with:
|
||||
* where:
|
||||
*
|
||||
* A(N) = Σ_(k = 1)^(k_max) (N^k/k!)² * H(k)
|
||||
* B(N) = Σ_(k = 0)^(k_max) (N^k/k!)²
|
||||
* C(N) = 1/4N Σ_(k = 0)^(2N) ((2k)!)^3/((k!)^4*(16N))^2k
|
||||
* H(k) = Σ_(j = 1)^(k) (1/k), the k-th harmonic number
|
||||
*
|
||||
* where N is computed from D as written below and k_max is
|
||||
* currently 5*N, chosen in a completely arbitrary way.
|
||||
* The error of the estimation is given by
|
||||
*
|
||||
* source: http://www.numberworld.org/y-cruncher/internals/formulas.html
|
||||
* Δ(N) = 5/12 √(2π) exp(-8N)/√x
|
||||
*
|
||||
* so, given a number D of decimal digits to compute we ask
|
||||
* that the error be smaller than 10^-D. The smallest integer
|
||||
* to solve the inequality can be proven to be
|
||||
*
|
||||
* N = floor(W(5π/9 10^(2D + 1))/16) + 1
|
||||
*
|
||||
* where W is the principal value of the Lambert W function.
|
||||
*
|
||||
* The number of series terms needed to reduce the error, due to
|
||||
* to less than 10^-D is α⋅2^p where α is the solution of:
|
||||
*
|
||||
* α log(α) = 3 + α ⇒ α = exp(W(3e) - 1) ≈ 4.97
|
||||
*
|
||||
* source: Precise error estimate of the Brent-McMillan algorithm for
|
||||
* the computation of Euler's constant, Jean-Pierre Demailly.
|
||||
*/
|
||||
|
||||
/* `mpq_dec_str(z, n)`
|
||||
@ -490,6 +505,11 @@ int main(int argc, char** argv) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// requested decimal digits
|
||||
size_t D = atol(argv[1]);
|
||||
// Brent-McMillan number N
|
||||
size_t N = gsl_sf_lambert_W0(5*M_PI/9*pow(10, 2*D + 1))/16 + 1;
|
||||
|
||||
mpq_t exact, indicator;
|
||||
mpq_inits(exact, indicator, NULL);
|
||||
|
||||
@ -525,11 +545,6 @@ int main(int argc, char** argv) {
|
||||
"137174210/1111111111",
|
||||
10);
|
||||
|
||||
// number of decimal places
|
||||
size_t D = atol(argv[1]);
|
||||
// number of terms
|
||||
size_t N = 10 + floor(1.0/8 * log(10) * (double)D);
|
||||
|
||||
fprintf(stderr, "[main] N: %ld\n", N);
|
||||
|
||||
/* calculate series */
|
||||
|
70
ex-2/fancy.c
70
ex-2/fancy.c
@ -1,24 +1,40 @@
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <gsl/gsl_sf_lambert.h>
|
||||
|
||||
// The Euler-Mascheroni constant is computed through the formula:
|
||||
//
|
||||
// γ = A(N)/B(N) - ln(N)
|
||||
//
|
||||
// with:
|
||||
//
|
||||
// A(N) = Σ_(k = 1)^(k_max) (N^k/k!) * H(k)
|
||||
// B(N) = Σ_(k = 0)^(k_max) (N^k/k!)
|
||||
// H(k) = Σ_(j = 1)^(k) (1/k)
|
||||
//
|
||||
// where N is computed from D as written below and k_max is the value
|
||||
// at which there is no difference between two consecutive terms of
|
||||
// the sum because of double precision.
|
||||
//
|
||||
// source: http://www.numberworld.org/y-cruncher/internals/formulas.html
|
||||
/* The Euler-Mascheroni constant is computed to D decimal
|
||||
* places using the refined Brent-McMillan formula:
|
||||
*
|
||||
* γ = A(N)/B(N) - C(N)/B(N)² - log(N)
|
||||
*
|
||||
* where:
|
||||
*
|
||||
* A(N) = Σ_(k = 1)^(k_max) (N^k/k!)² * H(k)
|
||||
* B(N) = Σ_(k = 0)^(k_max) (N^k/k!)²
|
||||
* C(N) = 1/4N Σ_(k = 0)^(2N) ((2k)!)^3/((k!)^4*(16N))^2k
|
||||
* H(k) = Σ_(j = 1)^(k) (1/k), the k-th harmonic number
|
||||
*
|
||||
* The error of the estimation is given by
|
||||
*
|
||||
* Δ(N) = 5/12 √(2π) exp(-8N)/√x
|
||||
*
|
||||
* so, given a number D of decimal digits to compute we ask
|
||||
* that the error be smaller than 10^-D. The smallest integer
|
||||
* to solve the inequality can be proven to be
|
||||
*
|
||||
* N = floor(W(5π/9 10^(2D + 1))/16) + 1
|
||||
*
|
||||
* where W is the principal value of the Lambert W function.
|
||||
*
|
||||
* The series are truncated at k_max, which is when the difference
|
||||
* between two consecutive terms of the sum is zero, at double precision.
|
||||
*
|
||||
* source: Precise error estimate of the Brent-McMillan algorithm for
|
||||
* the computation of Euler's constant, Jean-Pierre Demailly.
|
||||
*/
|
||||
|
||||
// Partial harmonic sum h
|
||||
// Partial harmonic sum H
|
||||
double harmonic_sum(double n) {
|
||||
double sum = 0;
|
||||
for (double k = 1; k < n+1; k++) {
|
||||
@ -49,6 +65,7 @@ double b_series(int N){
|
||||
return sum;
|
||||
}
|
||||
|
||||
// C series
|
||||
double c_series(int N) {
|
||||
double sum = 0;
|
||||
for (double k = 0; k < N; k++) {
|
||||
@ -57,23 +74,28 @@ double c_series(int N) {
|
||||
return sum/(4.0*N);
|
||||
}
|
||||
|
||||
// Takes in input the number D of desired correct decimals
|
||||
// Best result obtained with D = 15, N = 10
|
||||
|
||||
/* The Best result is obtained with D=15, which accurately
|
||||
* computes 15 digits and gives an error of 3.3e-16.
|
||||
*
|
||||
* Increasing to D=21 decreases the error to a minimum of
|
||||
* 1.1e-16 but the program can't achieve the requested D.
|
||||
*/
|
||||
int main(int argc, char** argv) {
|
||||
double exact =
|
||||
0.57721566490153286060651209008240243104215933593992;
|
||||
|
||||
// If no argument is given is input, an error signal is displayed
|
||||
// and the program quits
|
||||
|
||||
/* if no argument is given, show the usage */
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "usage: %s D\n", argv[0]);
|
||||
fprintf(stderr, "Computes γ up to D decimal places.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int N = floor(2.0 + 1.0/4 * log(10) * (double)atoi(argv[1]));
|
||||
// requested decimal digits
|
||||
int D = atoi(argv[1]);
|
||||
// Brent-McMillan number N
|
||||
int N = gsl_sf_lambert_W0(5*M_PI/9*pow(10, 2*D + 1))/16 + 1;
|
||||
|
||||
double A = a_series(N);
|
||||
double B = b_series(N);
|
||||
double C = c_series(N);
|
||||
|
Loading…
Reference in New Issue
Block a user