The Finite Resolution Dynamics Software
opensine.h
Go to the documentation of this file.
1 /////////////////////////////////////////////////////////////////////////////
2 ///
3 /// @file opensine.h
4 ///
5 /// An open-interval version of the sine function.
6 /// This file contains the definition of the sine function in the interval
7 /// arithmetic based on open intervals, as opposed to closed intervals.
8 /// Based on this definition, also the other trigonometric functions
9 /// can be implemented.
10 ///
11 /// @author Pawel Pilarczyk
12 ///
13 /////////////////////////////////////////////////////////////////////////////
14 
15 // Copyright (C) 2007-2010 by Pawel Pilarczyk.
16 //
17 // This file is part of my research program package. This is free software;
18 // you can redistribute it and/or modify it under the terms of the GNU
19 // General Public License as published by the Free Software Foundation;
20 // either version 2 of the License, or (at your option) any later version.
21 //
22 // This software is distributed in the hope that it will be useful,
23 // but WITHOUT ANY WARRANTY; without even the implied warranty of
24 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 // GNU General Public License for more details.
26 //
27 // You should have received a copy of the GNU General Public License along
28 // with this software; see the file "license.txt". If not, write to the
29 // Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
30 // MA 02111-1307, USA.
31 
32 // Started on December 28, 2008. Last revision: December 30, 2008.
33 
34 
35 #ifndef _FINRESDYN_OPENSINE_H_
36 #define _FINRESDYN_OPENSINE_H_
37 
38 
39 // include local header files
40 #include "rounding.h"
41 #include "numberpi.h"
42 
43 
44 // --------------------------------------------------
45 // ---- a class that computes the sine function -----
46 // --------------------------------------------------
47 
48 /// Computes the sine function using the expansion
49 /// into the Taylor series at pi/2.
50 template <class NumType, int nTerms>
51 class tOpenSine
52 {
53 public:
54  /// Computes the sine function in the open interval arithmetic.
55  static int compute (const NumType &xLeft, const NumType &xRight,
56  NumType &yLeft, NumType &yRight);
57 
58 private:
59  /// Computes the provided number of terms in the expansion
60  /// of sin(pi+x) in the Taylor series at pi.
61  static void computeTaylorTerms (const NumType &x,
62  NumType *lower_x2k, NumType *upper_x2k, int n);
63 
64  /// Returns a lower bound for the sine function.
65  /// The argument must be in the interval (0,pi).
66  static NumType lowerBoundTaylor (const NumType &x);
67 
68  /// Returns an upper bound for the sine function.
69  /// The argument must be in the interval (0,pi).
70  static NumType upperBoundTaylor (const NumType &x);
71 
72  /// Computes the sine function in the open interval arithmetic
73  /// with the arguments shifted so that xLeft is in [0,pi].
74  static int shifted1pi (const NumType &xLeft, const NumType &xRight,
75  NumType &yLeft, NumType &yRight);
76 
77  /// Computes the sine function in the open interval arithmetic
78  /// with the arguments shifted so that xLeft is in [0,2pi].
79  static int shifted2pi (const NumType &xLeft, const NumType &xRight,
80  NumType &yLeft, NumType &yRight);
81 
82 }; /* class tOpenSine */
83 
84 // --------------------------------------------------
85 
86 template <class NumType, int nTerms>
88  (const NumType &x, NumType *lower_x2k, NumType *upper_x2k, int n)
89 {
90  // prepare shortcuts for the rounding mode and the number pi
91  typedef tRounding<NumType> rnd;
92  typedef tNumberPi<NumType> pi;
93 
94  // prepare a lower bound and an upper bound for x^2
95  NumType lowerDiff = (x <= pi::lowerBound () / 2) ?
96  rnd::sub_down (pi::lowerBound () / 2, x) :
97  rnd::sub_down (x, pi::upperBound () / 2);
98  if (lowerDiff < 0)
99  lowerDiff = 0;
100  NumType lower_x2 = rnd::mul_down (lowerDiff, lowerDiff);
101  NumType upperDiff = (x <= pi::upperBound () / 2) ?
102  rnd::sub_up (pi::upperBound () / 2, x) :
103  rnd::sub_up (x, pi::lowerBound () / 2);
104  NumType upper_x2 = rnd::mul_up (upperDiff, upperDiff);
105 
106  // compute the terms, including the factorial, but without the sign,
107  // for the Taylor expansion as in the following formula:
108  // \sin (\pi/2 + x) = \sum_{k = 0}^{\infty} (-1)^k x^{2k} / (2k)!
109  for (int k = 0; k < n; ++ k)
110  {
111  if (k == 0)
112  {
113  lower_x2k [0] = 1;
114  upper_x2k [0] = 1;
115  }
116  else if (k == 1)
117  {
118  lower_x2k [1] = lower_x2 / 2;
119  upper_x2k [1] = upper_x2 / 2;
120  }
121  else
122  {
123  long int factor = ((k << 1) - 1) * (k << 1);
124  lower_x2k [k] = rnd::mul_down (lower_x2k [k - 1],
125  lower_x2);
126  lower_x2k [k] = rnd::div_down (lower_x2k [k],
127  factor);
128  upper_x2k [k] = rnd::mul_up (upper_x2k [k - 1],
129  upper_x2);
130  upper_x2k [k] = rnd::div_up (upper_x2k [k],
131  factor);
132  }
133  }
134 
135  return;
136 } /* computeTaylorTerms */
137 
138 template <class NumType, int nTerms>
139 inline NumType tOpenSine<NumType,nTerms>::lowerBoundTaylor (const NumType &x)
140 {
141  // prepare a shortcut for the rounding mode
142  typedef tRounding<NumType> rnd;
143 
144  // determine an even strict upper bound for the number of terms
145  int n = (nTerms & 1) ? (nTerms + 1) : (nTerms);
146 
147  // prepare all the terms to add with factorials included
148  NumType lower_x2k [nTerms + 1];
149  NumType upper_x2k [nTerms + 1];
150  computeTaylorTerms (x, lower_x2k, upper_x2k, n);
151 
152  // compute the sum for the given number of terms
153  NumType result = 0;
154  for (int k = n - 1; k >= 0; -- k)
155  {
156  // subtract the term if its number is odd
157  if (k & 1)
158  result = rnd::sub_down (result, upper_x2k [k]);
159 
160  // add the term if its number is even
161  else
162  result = rnd::add_down (result, lower_x2k [k]);
163  }
164 
165  return result;
166 } /* lowerBoundTaylor */
167 
168 template <class NumType, int nTerms>
169 inline NumType tOpenSine<NumType,nTerms>::upperBoundTaylor (const NumType &x)
170 {
171  // prepare a shortcut for the rounding mode
172  typedef tRounding<NumType> rnd;
173 
174  // determine an odd strict upper bound for the number of terms
175  int n = (nTerms & 1) ? (nTerms) : (nTerms + 1);
176 
177  // prepare all the terms to add with factorials included
178  NumType lower_x2k [nTerms + 1];
179  NumType upper_x2k [nTerms + 1];
180  computeTaylorTerms (x, lower_x2k, upper_x2k, n);
181 
182  // compute the sum for the given number of terms
183  NumType result = 0;
184  for (int k = n - 1; k >= 0; -- k)
185  {
186  // subtract the term if its number is odd
187  if (k & 1)
188  result = rnd::sub_up (result, lower_x2k [k]);
189 
190  // add the term if its number is even
191  else
192  result = rnd::add_up (result, upper_x2k [k]);
193  }
194 
195  return result;
196 } /* upperBoundTaylor */
197 
198 // --------------------------------------------------
199 
200 template <class NumType, int nTerms>
202  (const NumType &xLeft, const NumType &xRight,
203  NumType &yLeft, NumType &yRight)
204 {
205  // prepare shortcuts for the rounding mode and the number pi
206  typedef tRounding<NumType> rnd;
207  typedef tNumberPi<NumType> pi;
208 
209  // prepare bounds for the number 2pi
210  NumType lower2pi = pi::lowerBound () * 2;
211  NumType upper2pi = pi::upperBound () * 2;
212 
213  // shift the interval so that the left bound is in [0,2pi]
214  if (xLeft < 0)
215  {
216  int periods = static_cast<int> ((-xLeft) / upper2pi) + 1;
217  NumType shiftMin = rnd::mul_down (periods, lower2pi);
218  NumType shiftMax = rnd::mul_up (periods, upper2pi);
219  NumType newLeft = rnd::add_down (xLeft, shiftMin);
220  NumType newRight = rnd::add_up (xRight, shiftMax);
221  while (newLeft < 0)
222  {
223  // std::cout << "Debug: sine add (" << newLeft << ").\n";
224  newLeft = rnd::add_down (newLeft, lower2pi);
225  newRight = rnd::add_up (newRight, upper2pi);
226  }
227  while (newLeft > lower2pi)
228  {
229  // std::cout << "Debug: sine sub (" << newLeft << ").\n";
230  newLeft = rnd::sub_down (newLeft, lower2pi);
231  newRight = rnd::sub_up (newRight, upper2pi);
232  }
233  return shifted2pi (newLeft, newRight, yLeft, yRight);
234  }
235  else if (xLeft > upper2pi)
236  {
237  int periods = static_cast<int> (xLeft / upper2pi);
238  NumType shiftMin = rnd::mul_down (periods, lower2pi);
239  NumType shiftMax = rnd::mul_up (periods, upper2pi);
240  NumType newLeft = rnd::sub_down (xLeft, shiftMax);
241  NumType newRight = rnd::sub_up (xRight, shiftMin);
242  while (newLeft > upper2pi)
243  {
244  // std::cout << "Debug: sine+sub (" << newLeft << ").\n";
245  newLeft = rnd::sub_down (newLeft, upper2pi);
246  newRight = rnd::sub_up (newRight, lower2pi);
247  }
248  return shifted2pi (newLeft, newRight, yLeft, yRight);
249  }
250  else
251  {
252  return shifted2pi (xLeft, xRight, yLeft, yRight);
253  }
254 } /* compute */
255 
256 template <class NumType, int nTerms>
258  (const NumType &xLeft, const NumType &xRight,
259  NumType &yLeft, NumType &yRight)
260 {
261  // prepare shortcuts for the rounding mode and the number pi
262  typedef tRounding<NumType> rnd;
263  typedef tNumberPi<NumType> pi;
264 
265  // prepare bounds for the number pi
266  const NumType lowerPi = pi::lowerBound ();
267  const NumType upperPi = pi::upperBound ();
268 
269  // prepare bounds for the number 2pi
270  const NumType lower2pi = lowerPi * 2;
271  const NumType upper2pi = upperPi * 2;
272 
273  // check if the width of the interval exceeds the period of sine
274 // if (rnd::sub_up (xRight, xLeft) >= lower2pi)
275 // {
276 // yLeft = rnd::sub_down (-1, rnd::min_number ());
277 // yRight = rnd::add_up (1, rnd::min_number ());
278 // return 0;
279 // }
280 
281  // prepare bounds for the number pi/2
282  const NumType lowerPi2 = lowerPi / 2;
283  const NumType upperPi2 = upperPi / 2;
284 
285  // if both bounds are to the left from pi/2
286  if (xRight <= upperPi2)
287  {
288  yLeft = lowerBoundTaylor (xLeft);
289  yRight = upperBoundTaylor (xRight);
290  return 0;
291  }
292 
293  // prepare bounds for the number 3pi/2
294  NumType upper3pi2 = rnd::add_up (upperPi, upperPi2);
295 // NumType lower3pi2 = rnd::add_down (lowerPi, lowerPi2);
296 
297  // if the left bound is below pi/2, but the right bound is larger
298  if (xLeft <= upperPi2)
299  {
300  // if the right bound is in [pi/2, pi]
301  if (xRight <= upperPi)
302  {
303  if (rnd::sub_up (upperPi2, xLeft) <=
304  rnd::sub_down (xRight, upperPi2))
305  {
306  yLeft = lowerBoundTaylor (xRight);
307  }
308  else if (rnd::sub_down (lowerPi2, xLeft) >=
309  rnd::sub_up (xRight, lowerPi2))
310  {
311  yLeft = lowerBoundTaylor (xLeft);
312  }
313  else
314  {
315  NumType leftVal = lowerBoundTaylor (xLeft);
316  NumType rightVal = lowerBoundTaylor (xRight);
317  yLeft = (leftVal < rightVal) ?
318  leftVal : rightVal;
319  }
320  yRight = rnd::add_up (1, rnd::min_number ());
321  return 0;
322  }
323 
324  // if the right bound is in [pi, 3/2 pi]
325  if (xRight <= upper3pi2)
326  {
327  yLeft = -upperBoundTaylor
328  (rnd::sub_up (xRight, lowerPi));
329  yRight = rnd::add_up (1, rnd::min_number ());
330  return 0;
331  }
332 
333  // if the right bound is beyond 3/2 pi
334  yLeft = rnd::sub_down (-1, rnd::min_number ());
335  yRight = rnd::add_up (1, rnd::min_number ());
336  return 0;
337  }
338 
339  // if the left bound is between pi/2 and pi
340 // if (xLeft <= upperPi)
341  {
342  // if the right bound is also between pi/2 and pi
343  if (xRight <= upperPi)
344  {
345  yLeft = lowerBoundTaylor (xRight);
346  yRight = upperBoundTaylor (xLeft);
347  return 0;
348  }
349 
350  // if the right bound is in [pi, 3/2 pi]
351  if (xRight <= upper3pi2)
352  {
353  yLeft = -upperBoundTaylor
354  (rnd::sub_up (xRight, lowerPi));
355  yRight = upperBoundTaylor (xLeft);
356  return 0;
357  }
358 
359  // if the right bound is in [3/2 pi, 2pi]
360  if (xRight <= upper2pi)
361  {
362  yLeft = rnd::sub_down (-1, rnd::min_number ());
363  yRight = upperBoundTaylor (xLeft);
364  return 0;
365  }
366 
367  // if the right bound is in [2pi, 2pi+pi/2]
368  if (xRight <= rnd::add_up (upper2pi, upperPi2))
369  {
370  if (rnd::sub_up (upperPi, xLeft) <=
371  rnd::sub_down (xRight, upper2pi))
372  {
373  yRight = upperBoundTaylor
374  (rnd::sub_up (xRight, lower2pi));
375  }
376  else if (rnd::sub_down (lowerPi, xLeft) >=
377  rnd::sub_up (xRight, lower2pi))
378  {
379  yRight = upperBoundTaylor (xLeft);
380  }
381  else
382  {
383  NumType leftVal = upperBoundTaylor (xLeft);
384  NumType rightVal = upperBoundTaylor
385  (rnd::sub_up (xRight, lower2pi));
386  yRight = (leftVal < rightVal) ?
387  rightVal : leftVal;
388  }
389  yLeft = rnd::sub_down (-1, rnd::min_number ());
390  return 0;
391  }
392 
393  // otherwise the full range of values is covered
394  yLeft = rnd::sub_down (-1, rnd::min_number ());
395  yRight = rnd::add_up (1, rnd::min_number ());
396  return 0;
397  }
398 } /* shifted1pi */
399 
400 template <class NumType, int nTerms>
402  (const NumType &xLeft, const NumType &xRight,
403  NumType &yLeft, NumType &yRight)
404 {
405  // prepare shortcuts for the rounding mode and the number pi
406  typedef tRounding<NumType> rnd;
407  typedef tNumberPi<NumType> pi;
408 
409  // if the left endpoint is below pi then fine
410  if (xLeft <= pi::upperBound ())
411  return shifted1pi (xLeft, xRight, yLeft, yRight);
412 
413  // compute the function with the arguments shifted by pi
414  // (note the reversed order of results as they will be negated)
415  shifted1pi (rnd::sub_down (xLeft, pi::upperBound ()),
416  rnd::sub_up (xRight, pi::upperBound ()),
417  yRight, yLeft);
418 
419  // flip the resulting interval and finish
420  yLeft = -yLeft;
421  yRight = -yRight;
422  return 0;
423 } /* shifted2pi */
424 
425 
426 #endif // _FINRESDYN_OPENSINE_H_
427 
Rigorous bounds for the number pi.
Rigorous rounding of arithmetic operations.
static NumType lowerBoundTaylor(const NumType &x)
Returns a lower bound for the sine function.
Definition: opensine.h:139
static void computeTaylorTerms(const NumType &x, NumType *lower_x2k, NumType *upper_x2k, int n)
Computes the provided number of terms in the expansion of sin(pi+x) in the Taylor series at pi...
Definition: opensine.h:88
static int shifted1pi(const NumType &xLeft, const NumType &xRight, NumType &yLeft, NumType &yRight)
Computes the sine function in the open interval arithmetic with the arguments shifted so that xLeft i...
Definition: opensine.h:258
static int shifted2pi(const NumType &xLeft, const NumType &xRight, NumType &yLeft, NumType &yRight)
Computes the sine function in the open interval arithmetic with the arguments shifted so that xLeft i...
Definition: opensine.h:402
A class for rounding operations which uses the BOOST library.
Definition: rounding.h:48
static NumType upperBoundTaylor(const NumType &x)
Returns an upper bound for the sine function.
Definition: opensine.h:169
Computes the sine function using the expansion into the Taylor series at pi/2.
Definition: opensine.h:51
A class that holds the number pi.
Definition: numberpi.h:48
static int compute(const NumType &xLeft, const NumType &xRight, NumType &yLeft, NumType &yRight)
Computes the sine function in the open interval arithmetic.
Definition: opensine.h:202