You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

512 lines
11 KiB

/*
* tests/qd_test.cpp
*
* This work was supported by the Director, Office of Science, Division
* of Mathematical, Information, and Computational Sciences of the
* U.S. Department of Energy under contract number DE-AC03-76SF00098.
*
* Copyright (c) 2000-2001
*
* This contains some simple tests to sanity check the double-double
* and quad-double library.
*/
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <qd/qd_real.h>
#include <qd/fpu.h>
using std::cout;
using std::cerr;
using std::endl;
using std::abs;
using std::sqrt;
using std::strcmp;
using std::exit;
// Global flags passed to the main program.
static bool flag_test_dd = false;
static bool flag_test_qd = false;
bool flag_verbose = false;
bool print_result(bool result) {
if (result)
cout << "Test passed." << endl;
else
cout << "Test FAILED." << endl;
return result;
}
template <class T>
class TestSuite {
static const int double_digits;
public:
bool test1();
bool test2();
bool test3();
bool test4();
bool test5();
bool test6();
bool test7();
bool test8();
bool testall();
};
template <class T>
const int TestSuite<T>::double_digits = 6;
/* Test 1. Polynomial Evaluation / Polynomial Solving */
template <class T>
bool TestSuite<T>::test1() {
cout << endl;
cout << "Test 1. (Polynomial)." << endl;
static const int n = 8;
T *c = new T[n];
T x, y;
for (int i = 0; i < n; i++)
c[i] = static_cast<double>(i+1);
x = polyroot(c, n-1, T(0.0));
y = polyeval(c, n-1, x);
if (flag_verbose) {
cout.precision(T::_ndigits);
cout << "Root Found: x = " << x << endl;
cout << " p(x) = " << y << endl;
}
delete [] c;
return (to_double(y) < 4.0 * T::_eps);
}
/* Test 2. Machin's Formula for Pi. */
template <class T>
bool TestSuite<T>::test2() {
cout << endl;
cout << "Test 2. (Machin's Formula for Pi)." << endl;
/* Use the Machin's arctangent formula:
pi / 4 = 4 arctan(1/5) - arctan(1/239)
The arctangent is computed based on the Taylor series expansion
arctan(x) = x - x^3 / 3 + x^5 / 5 - x^7 / 7 + ...
*/
T s1, s2, t, r;
int k;
int sign;
double d;
double err;
/* Compute arctan(1/5) */
d = 1.0;
t = T(1.0) / 5.0;
r = sqr(t);
s1 = 0.0;
k = 0;
sign = 1;
while (t > T::_eps) {
k++;
if (sign < 0)
s1 -= (t / d);
else
s1 += (t / d);
d += 2.0;
t *= r;
sign = -sign;
}
if (flag_verbose)
cout << k << " Iterations" << endl;
/* Compute arctan(1/239) */
d = 1.0;
t = T(1.0) / 239.0;
r = sqr(t);
s2 = 0.0;
k = 0;
sign = 1;
while (t > T::_eps) {
k++;
if (sign < 0)
s2 -= (t / d);
else
s2 += (t / d);
d += 2.0;
t *= r;
sign = -sign;
}
if (flag_verbose)
cout << k << " Iterations" << endl;
T p = 4.0 * s1 - s2;
p *= 4.0;
err = abs(to_double(p - T::_pi));
if (flag_verbose) {
cout.precision(T::_ndigits);
cout << " pi = " << p << endl;
cout << " _pi = " << T::_pi << endl;
cout.precision(double_digits);
cout << "error = " << err << " = " << err / T::_eps << " eps" << endl;
}
return (err < 8.0 * T::_eps);
}
/* Test 3. Salamin-Brent Quadratic Formula for Pi. */
template <class T>
bool TestSuite<T>::test3() {
cout << endl;
cout << "Test 3. (Salamin-Brent Quadratic Formula for Pi)." << endl;
cout.precision(T::_ndigits);
T a, b, s, p;
T a_new, b_new, p_old;
double m;
double err;
const int max_iter = 20;
a = 1.0;
b = sqrt(T(0.5));
s = 0.5;
m = 1.0;
p = 2.0 * sqr(a) / s;
if (flag_verbose)
cout << "Iteration 0: " << p << endl;
for (int i = 1; i <= max_iter; i++) {
m *= 2.0;
a_new = 0.5 * (a + b);
b_new = a * b;
s -= m * (sqr(a_new) - b_new);
a = a_new;
b = sqrt(b_new);
p_old = p;
p = 2.0 * sqr(a) / s;
if (flag_verbose)
cout << "Iteration " << std::setw(2) << i << ": " << p << endl;
if (abs(to_double(p - p_old)) < 64 * T::_eps)
break;
}
err = abs(to_double(p - T::_pi));
if (flag_verbose) {
cout << " _pi: " << T::_pi << endl;
cout.precision(double_digits);
cout << " error: " << err << " = " << err / T::_eps << " eps" << endl;
}
// for some reason, this test gives relatively large error compared
// to other tests. May need to be looked at more closely.
return (err < 1024.0 * T::_eps);
}
/* Test 4. Borwein Quartic Formula for Pi. */
template <class T>
bool TestSuite<T>::test4() {
cout << endl;
cout << "Test 4. (Borwein Quartic Formula for Pi)." << endl;
cout.precision(T::_ndigits);
T a, y, p, r, p_old;
double m;
double err;
const int max_iter = 20;
a = 6.0 - 4.0 * sqrt(T(2.0));
y = sqrt(T(2.0)) - 1.0;
m = 2.0;
p = 1.0 / a;
if (flag_verbose)
cout << "Iteration 0: " << p << endl;
for (int i = 1; i <= max_iter; i++) {
m *= 4.0;
r = nroot(1.0 - sqr(sqr(y)), 4);
y = (1.0 - r) / (1.0 + r);
a = a * sqr(sqr(1.0 + y)) - m * y * (1.0 + y + sqr(y));
p_old = p;
p = 1.0 / a;
if (flag_verbose)
cout << "Iteration " << std::setw(2) << i << ": " << p << endl;
if (abs(to_double(p - p_old)) < 16 * T::_eps)
break;
}
err = abs(to_double(p - T::_pi));
if (flag_verbose) {
cout << " _pi: " << T::_pi << endl;
cout.precision(double_digits);
cout << " error: " << err << " = " << err / T::_eps << " eps" << endl;
}
return (err < 256.0 * T::_eps);
}
/* Test 5. Taylor Series Formula for E. */
template <class T>
bool TestSuite<T>::test5() {
cout << endl;
cout << "Test 5. (Taylor Series Formula for E)." << endl;
cout.precision(T::_ndigits);
/* Use Taylor series
e = 1 + 1 + 1/2! + 1/3! + 1/4! + ...
To compute e.
*/
T s = 2.0, t = 1.0;
double n = 1.0;
double delta;
int i = 0;
while (t > T::_eps) {
i++;
n += 1.0;
t /= n;
s += t;
}
delta = abs(to_double(s - T::_e));
if (flag_verbose) {
cout << " e = " << s << endl;
cout << " _e = " << T::_e << endl;
cout.precision(double_digits);
cout << "error = " << delta << " = " << delta / T::_eps << " eps" << endl;
cout << i << " iterations." << endl;
}
return (delta < 64.0 * T::_eps);
}
/* Test 6. Taylor Series Formula for log 2.*/
template <class T>
bool TestSuite<T>::test6() {
cout << endl;
cout << "Test 6. (Taylor Series Formula for Log 2)." << endl;
cout.precision(T::_ndigits);
/* Use the Taylor series
-log(1-x) = x + x^2/2 + x^3/3 + x^4/4 + ...
with x = 1/2 to get log(1/2) = -log 2.
*/
T s = 0.5;
T t = 0.5;
double delta;
double n = 1.0;
double i = 0;
while (abs(t) > T::_eps) {
i++;
n += 1.0;
t *= 0.5;
s += (t/n);
}
delta = abs(to_double(s - T::_log2));
if (flag_verbose) {
cout << " log2 = " << s << endl;
cout << "_log2 = " << T::_log2 << endl;
cout.precision(double_digits);
cout << "error = " << delta << " = " << (delta / T::_eps)
<< " eps" << endl;
cout << i << " iterations." << endl;
}
return (delta < 4.0 * T::_eps);
}
/* Test 7. Sanity check for exp. */
template <class T>
bool TestSuite<T>::test7() {
cout << endl;
cout << "Test 7. (Sanity check for exp)." << endl;
cout.precision(T::_ndigits);
/* Do simple sanity check
*
* e^2 = exp(2)
* = exp(-13/4) * exp(-9/4) * exp(-5/4) * exp(-1/4) *
* exp(3/4) * exp(7/4) * exp(11/4) * exp(15/4)
*/
T t = -3.25;
T p = 1.0;
for (int i = 0; i < 8; i++, t += 1.0) {
/* For some reason gcc-4.1.x on x86_64 miscompiles p *= exp(t) here. */
p = p * exp(t);
}
T t1 = exp(T(2.0));
T t2 = sqr(T::_e);
double delta = std::max(abs(to_double(t1 - p)), abs(to_double(t2 - p)));
if (flag_verbose) {
cout << "result = " << p << endl;
cout << "exp(2) = " << t1 << endl;
cout << " e^2 = " << t2 << endl;
cout.precision(double_digits);
cout << " error = " << delta << " = " << (delta / T::_eps)
<< " eps" << endl;
}
return (delta < 16.0 * T::_eps);
}
template <class T>
bool TestSuite<T>::test8() {
cout << endl;
cout << "Test 8. (Sanity check for sin / cos)." << endl;
cout.precision(T::_ndigits);
/* Do simple sanity check
*
* sin(x) = sin(5x/7)cos(2x/7) + cos(5x/7)sin(2x/7)
*
* cos(x) = cos(5x/7)cos(2x/7) - sin(5x/7)sin(2x/7);
*/
T x = T::_pi / 3.0;
T x1 = 5.0 * x / 7.0;
T x2 = 2.0 * x / 7.0;
T r1 = sin(x1)*cos(x2) + cos(x1)*sin(x2);
T r2 = cos(x1)*cos(x2) - sin(x1)*sin(x2);
T t1 = sqrt(T(3.0)) / 2.0;
T t2 = 0.5;
double delta = std::max(abs(to_double(t1 - r1)), abs(to_double(t2 - r2)));
if (flag_verbose) {
cout << " r1 = " << r1 << endl;
cout << " t1 = " << t1 << endl;
cout << " r2 = " << r2 << endl;
cout << " t2 = " << t2 << endl;
cout.precision(double_digits);
cout << " error = " << delta << " = " << (delta / T::_eps)
<< " eps" << endl;
}
return (delta < 4.0 * T::_eps);
}
template <class T>
bool TestSuite<T>::testall() {
bool pass = true;
pass &= print_result(test1());
pass &= print_result(test2());
pass &= print_result(test3());
pass &= print_result(test4());
pass &= print_result(test5());
pass &= print_result(test6());
pass &= print_result(test7());
pass &= print_result(test8());
return pass;
}
void print_usage() {
cout << "qd_test [-h] [-dd] [-qd] [-all]" << endl;
cout << " Performs miscellaneous tests of the quad-double library," << endl;
cout << " such as polynomial root finding, computation of pi, etc." << endl;
cout << endl;
cout << " -h -help Prints this usage message." << endl;
cout << " -dd Perform tests with double-double types." << endl;
cout << " -qd Perform tests with quad-double types." << endl;
cout << " This is the default." << endl;
cout << " -all Perform both double-double and quad-double tests." << endl;
cout << " -v" << endl;
cout << " -verbose Print detailed information for each test." << endl;
}
int main(int argc, char *argv[]) {
bool pass = true;
unsigned int old_cw;
fpu_fix_start(&old_cw);
/* Parse the arguments. */
char *arg;
for (int i = 1; i < argc; i++) {
arg = argv[i];
if (strcmp(arg, "-h") == 0 || strcmp(arg, "-help") == 0) {
print_usage();
exit(0);
} else if (strcmp(arg, "-dd") == 0) {
flag_test_dd = true;
} else if (strcmp(arg, "-qd") == 0) {
flag_test_qd = true;
} else if (strcmp(arg, "-all") == 0) {
flag_test_dd = flag_test_qd = true;
} else if (strcmp(arg, "-v") == 0 || strcmp(arg, "-verbose") == 0) {
flag_verbose = true;
} else {
cerr << "Unknown flag `" << arg << "'." << endl;
}
}
/* If no flag, test both double-double and quad-double. */
if (!flag_test_dd && !flag_test_qd) {
flag_test_dd = true;
flag_test_qd = true;
}
if (flag_test_dd) {
TestSuite<dd_real> dd_test;
cout << endl;
cout << "Testing dd_real ..." << endl;
if (flag_verbose)
cout << "sizeof(dd_real) = " << sizeof(dd_real) << endl;
pass &= dd_test.testall();
}
if (flag_test_qd) {
TestSuite<qd_real> qd_test;
cout << endl;
cout << "Testing qd_real ..." << endl;
if (flag_verbose)
cout << "sizeof(qd_real) = " << sizeof(qd_real) << endl;
pass &= qd_test.testall();
}
fpu_fix_end(&old_cw);
return (pass ? 0 : 1);
}