/*****************************************************/
/* */
/* Generator of Correctly Rounded Coefficients */
/* of the polynomial which stem from */
/* Chebychev Integration Formula */
/* based on Classical Error Estimation (CEE) Method */
/* */
/* Copyright (c) 2008- Tomonori Kouya */
/* */
/* Version 0.0: 2008-06-11 First published Version */
/* */
/* This program is free software: you can */
/* redistribute it and/or modify it under the terms */
/* of the GNU General Public License as published by */
/* the Free Software Foundation, either version 3 of */
/* the License, or any later version. */
/* */
/* This program is distributed in the hope that it */
/* will be useful, but WITHOUT ANY WARRANTY; without */
/* even the implied warranty of MERCHANTABILITY or */
/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
/* General Public License for more details. */
/* */
/* You should have received a copy of the GNU */
/* General Public License along with this program. */
/* If not, see . */
/* */
/* Please contact me via http://na-inet.jp/ */
/* if you have any comments or requests on this */
/* program. */
/* */
/* How to compile: */
/* $ cc [options] chevcoef.c -lgmp -lm */
/* */
/* Compile option: */
/* -DUSE_MPFR ... compute with mpfr library */
/* (link with -lmpfr) */
/* -DCHECK_CR ... with regorous check */
/* whether or not correctly rounded coefs */
/* */
/* -DPRINT_WARNING ... print relative errors */
/* and history of retry */
/* */
/* How to use: */
/* $ ./chevcoef degree correct_decimal_digits */
/* */
/*****************************************************/
#include
#include
#include "gmp.h"
#ifdef USE_MPFR
#include "mpfr.h"
#include "mpf2mpfr.h"
#endif
// Maximum degree of polynomial
#define MAX_DEGREE 32768
// for Arrays to keep coefficients of polynomial
#define MAX_DEGREE_P1 (MAX_DEGREE + 1)
// Max macro
#define MAX(i, j) (((i) > (j)) ? (i) : (j))
// using IEEE754 double ... so, it is unuseful.
/* coef[deg] * x^deg + ... + coef[1] * x + coef[0] */
void get_chevcoef(double coef[], long int deg)
{
long int i, j, index;
double sum;
coef[deg] = 1.0;
for(i = 1; i <= deg / 2; i++)
{
coef[deg - (i * 2 - 1)] = 0.0;
sum = 0;
for(j = 1; j <= i; j++)
sum += (1.0 / (2 * j + 1)) * coef[deg - 2 * (i - j)];
sum *= -deg / (2 * i);
coef[deg - i * 2] = sum;
}
}
//
/* coef[deg] * x^deg + ... + coef[1] * x + coef[0] */
void get_mpf_chevcoef(mpf_t coef[], long int deg)
{
long int i, j;
mpf_t sum, tmp;
mpf_init2(sum, mpf_get_prec(coef[0]));
mpf_init2(tmp, mpf_get_prec(coef[0]));
mpf_set_ui(coef[deg], 1UL);
for(i = 1; i <= deg / 2; i++)
{
mpf_set_ui(coef[deg - (i * 2 - 1)], 0UL);
mpf_set_ui(sum, 0UL);
for(j = 1; j <= i; j++)
{
mpf_set_ui(tmp, 1UL);
mpf_div_ui(tmp, tmp, 2 * j + 1);
mpf_mul(tmp, tmp, coef[deg - 2 * (i - j)]);
mpf_add(sum, sum, tmp);
}
mpf_mul_ui(sum, sum, deg);
mpf_div_ui(sum, sum, 2 * i);
mpf_neg(sum, sum);
mpf_set(coef[deg - i * 2], sum);
}
mpf_clear(sum);
mpf_clear(tmp);
}
// log10(a)
void _mpf_log10(mpf_t ret, mpf_t a)
{
mpf_t tmp;
long int a_2exp;
double a_double;
mpf_init2(tmp, mpf_get_prec(a));
// tmp = relerr * 2^relerr_2exp
#ifndef USE_MPFR
a_double = mpf_get_d_2exp(&a_2exp, a);
mpf_set_d(ret, log10(fabs(a_double)));
mpf_set_d(tmp, (double)a_2exp * log10(2.0));
mpf_add(ret, ret, tmp);
#else
mpfr_log10(ret, a, GMP_RNDN);
#endif
mpf_clear(tmp);
}
// CEE-based chevcoef
void print_cee_chevcoef(long int deg, unsigned long dprec)
{
unsigned long s, l, bs, bl, c, p[2], prec;
long int stop_flag, i, relerr_2exp;
double log10_relerr_max, relerr;
mpf_t mpfcoef_s[MAX_DEGREE_P1], mpfcoef_l[MAX_DEGREE_P1];
mpf_t tmp, rounded_s, rounded_l, tmp1;
// deg <= 33768
if((deg > MAX_DEGREE) || (dprec <= 0))
return;
// User-required precision
prec = (unsigned long)ceil(dprec / log10(2.0));
#ifdef CHECK_CR
mpf_init2(rounded_s, prec);
mpf_init2(rounded_l, prec);
#endif
mpf_init2(tmp1, prec);
// precision
log10_relerr_max = -(double)dprec;
c = MAX(dprec / 10, 10);
s = dprec + c;
l = s + c;
// additional precision
p[0] = 0; p[1] = 0;
// main loop
do {
stop_flag = 0;
// to binary
bs = (unsigned long)ceil(s / log10(2.0));
bl = (unsigned long)ceil(l / log10(2.0));
mpf_init2(tmp, bs);
for(i = 0; i <= deg; i++)
{
mpf_init2(mpfcoef_s[i], bs);
mpf_init2(mpfcoef_l[i], bl);
}
fprintf(stderr, "Additional digits: %d (%d bits)\n", MAX(p[0], p[1]), (unsigned long)ceil(MAX(p[0], p[1])/log10(2.0)));
fprintf(stderr, "Short digits: %d (%d bits)\n", s, bs);
get_mpf_chevcoef(mpfcoef_s, deg);
fprintf(stderr, "Long digits: %d (%d bits)\n", l, bl);
get_mpf_chevcoef(mpfcoef_l, deg);
// reset additional digits
p[0] = 0; p[1] = 0;
// check relative errors
for(i = 0; i <= deg; i++)
{
mpf_reldiff(tmp, mpfcoef_s[i], mpfcoef_l[i]);
mpf_abs(tmp, tmp);
if(mpf_cmp_ui(tmp, 0UL) == 0)
continue;
else
_mpf_log10(tmp, tmp);
// check relative error
if((mpf_cmp_ui(tmp, 0UL) != 0) && (mpf_cmp_d(tmp, log10_relerr_max) > 0))
{
#ifdef PRINT_WARNING
fprintf(stderr, "Too big relerr! :coef[%d] %7.1e < ", i, log10_relerr_max);
mpf_out_str(stderr, 10, 3, tmp);
fprintf(stderr, "\n");
#endif
stop_flag++;
// p[0] = max(log10(relerr(coef[i])) - log10(reltol))
mpf_set_d(tmp1, log10_relerr_max);
mpf_sub(tmp, tmp, tmp1);
if(p[0] < mpf_get_ui(tmp))
p[0] = mpf_get_ui(tmp);
}
#ifdef CHECK_CR
// check if correctly rounded
mpf_set(rounded_s, mpfcoef_s[i]);
mpf_set(rounded_l, mpfcoef_l[i]);
if(mpf_cmp(rounded_s, rounded_l) != 0)
{
#ifdef PRINT_WARNING
fprintf(stderr, "Not Correctly rounded!: coef[%d] reldiff = ", i);
mpf_reldiff(tmp, rounded_s, rounded_l);
mpf_out_str(stderr, 10, 2, tmp);
printf("\n");
#endif
stop_flag++;
// p[1] = max(log10(relerr(coef[i])) - log10(reltol))
mpf_set_d(tmp1, log10_relerr_max);
_mpf_log10(tmp, tmp);
mpf_sub(tmp, tmp, tmp1);
if(p[1] > mpf_get_ui(tmp))
p[1] = mpf_get_ui(tmp);
}
#endif
}
// retry ?
if(stop_flag <= 0)
break;
// reset
c += c + MAX(p[0], p[1]);
s += c;
l += c;
mpf_clear(tmp);
for(i = 0; i <= deg; i++)
{
mpf_clear(mpfcoef_s[i]);
mpf_clear(mpfcoef_l[i]);
}
} while(stop_flag > 0);
// print & clear
for(i = deg; i >= 0; i--)
{
printf("%d ", i);
mpf_out_str(stdout, 10, dprec, mpfcoef_l[i]);
printf("\n");
mpf_clear(mpfcoef_l[i]);
mpf_clear(mpfcoef_s[i]);
}
// clear
mpf_clear(tmp);
mpf_clear(tmp1);
#ifdef CHECK_CR
mpf_clear(rounded_s);
mpf_clear(rounded_l);
#endif
}
// usage
void usage(const char *progname)
{
printf("USAGE: %s DEGREE DECIMAL_DIGITS\n", progname);
printf(" ex: %s 100 50 -> 100th degree, 50 decimal digits\n", progname);
}
// Main function
int main(int argc, char *argv[])
{
//
if(argc <= 2)
{
usage(argv[0]);
exit(0);
}
print_cee_chevcoef((long int)atoi(argv[1]), (unsigned long)atoi(argv[2]));
return 0;
}