forked from FFTW/fftw3
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathtrigtest.c
195 lines (159 loc) · 4.39 KB
/
trigtest.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
/*
* program to test the accuracy of trig routines. Requires libpari or
* extended precision.
* This program is not part of fftw.
*/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#ifndef USE_PARI
#define USE_PARI 1
#endif
typedef double trigreal;
# define COS cos
# define SIN sin
# define TAN tan
# define KTRIG(x) (x)
static const trigreal MYK2PI =
KTRIG(6.2831853071795864769252867665590057683943388);
/**************************************************************/
/* copy-and-paste for kernel/trig.c */
#ifdef REALLY_ACCURATE
extern double fma (double X, double Y, double Z);
/* compute *x + *dx = a * b exactly */
static void dbmul(double a, double b, double *x, double *dx)
{
*x = a * b;
*dx = fma(a, b, -*x);
}
/* compute *x + *dx = a / b accurately */
static void dbdiv(double a, double b, double *x, double *dx)
{
*x = a / b;
*dx = fma(-*x, b, a) / b;
}
static double by2pi(double m, double n)
{
/* 2 PI rounded to IEEE-754 double precision */
static const double rpi2 =
6.28318530717958623199592693708837032318115234375;
/* 2 PI - rpi2 */
static const double rpi2r =
2.44929359829470635445213186455000211641949889184e-16;
double x, y, z, dx, dy, dz;
dbmul(rpi2, m, &x, &dx); /* x + dx = rpi2 * m, exactly */
dbmul(rpi2r, m, &y, &dy); /* x + dx = rpi2r * m, exactly */
/* x + dx ~ 2 PI * m, we lose roundoff in dx */
y += dx;
dx = y + dy;
dbdiv(x, n, &y, &dy); /* y + dy = x / n */
dbdiv(dx, n, &z, &dz); /* z + dz = dx / n */
return ((z + dy) + dz) + y;
}
#else
static const trigreal K2PI =
KTRIG(6.2831853071795864769252867665590057683943388);
#define by2pi(m, n) ((K2PI * (m)) / (n))
#endif
/*
* Improve accuracy by reducing x to range [0..1/8]
* before multiplication by 2 * PI.
*/
static trigreal sin2pi0(trigreal m, trigreal n);
static trigreal cos2pi0(trigreal m, trigreal n)
{
if (m < 0) return cos2pi0(-m, n);
if (m > n * 0.5) return cos2pi0(n - m, n);
if (m > n * 0.25) return -sin2pi0(m - n * 0.25, n);
if (m > n * 0.125) return sin2pi0(n * 0.25 - m, n);
return COS(by2pi(m, n));
}
static trigreal sin2pi0(trigreal m, trigreal n)
{
if (m < 0) return -sin2pi0(-m, n);
if (m > n * 0.5) return -sin2pi0(n - m, n);
if (m > n * 0.25) return cos2pi0(m - n * 0.25, n);
if (m > n * 0.125) return cos2pi0(n * 0.25 - m, n);
return SIN(by2pi(m, n));
}
trigreal cos2pi(int m, int n)
{
return cos2pi0((trigreal)m, (trigreal)n);
}
trigreal sin2pi(int m, int n)
{
return sin2pi0((trigreal)m, (trigreal)n);
}
trigreal tan2pi(int m, int n)
{
trigreal dm = m, dn = n;
/* unimplemented, unused */
return TAN(by2pi(dm, dn));
}
/**************************************************************/
/* test code */
trigreal naive_sin2pi(int m, int n)
{
return SIN(MYK2PI * ((trigreal) m / (trigreal) n));
}
trigreal naive_cos2pi(int m, int n)
{
return COS(MYK2PI * ((trigreal) m / (trigreal) n));
}
#if USE_PARI
#include <pari/pari.h>
long prec = 25;
double ck(long m, long n, double (*cf) (int, int), GEN(*gf) (GEN, long))
{
GEN gv, gcval, err, arg;
double cerr, cval;
long ltop = avma;
arg = mulsr(2L * m, divrs(gpi, n));
setlg(arg, prec);
gv = gf(arg, prec);
cval = cf(m, n);
gcval = dbltor(cval);
err = gsub(gcval, gv);
cerr = rtodbl(err);
avma = ltop;
return cerr;
}
#else
double ck(long m, long n, double (*cf) (int, int),
long double(*gf) (long double))
{
long double l2pi = 6.2831853071795864769252867665590057683943388L;
return cf(m, n) - gf(l2pi * (long double)m / (long double)n);
}
#define gsin sinl
#define gcos cosl
#endif
int main(int argc, char *argv[])
{
long nmin, nmax;
long n, m;
#if USE_PARI
pari_init(500000, 2);
mppi(prec);
#endif
if (argc > 1)
nmin = atoi(argv[1]);
else
nmin = 1024;
if (argc > 2)
nmax = atoi(argv[2]);
else
nmax = nmin;
for (n = nmin; n <= nmax; ++n) {
double maxe = 0.0, nmaxe = 0.0;;
for (m = 0; m < n; ++m) {
double e;
e = ck(m, n, sin2pi, gsin); if (e > maxe) maxe = e;
e = ck(m, n, cos2pi, gcos); if (e > maxe) maxe = e;
e = ck(m, n, naive_sin2pi, gsin); if (e > nmaxe) nmaxe = e;
e = ck(m, n, naive_cos2pi, gcos); if (e > nmaxe) nmaxe = e;
}
printf("%ld %g %g\n", n, maxe, nmaxe);
}
return 0;
}