mirror of
				https://github.com/saitohirga/WSJT-X.git
				synced 2025-10-27 11:00:32 -04:00 
			
		
		
		
	
		
			
	
	
		
			210 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			210 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
|  | // Copyright Christopher Kormanyos 2013.
 | ||
|  | // Distributed under the Boost Software License, Version 1.0.
 | ||
|  | // (See accompanying file LICENSE_1_0.txt or
 | ||
|  | // copy at http://www.boost.org/LICENSE_1_0.txt).
 | ||
|  | 
 | ||
|  | #ifdef _MSC_VER
 | ||
|  | #  pragma warning (disable : 4996) // assignment operator could not be generated.
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | # include <iostream>
 | ||
|  | # include <iomanip>
 | ||
|  | # include <limits>
 | ||
|  | # include <cmath>
 | ||
|  | 
 | ||
|  | #include <boost/static_assert.hpp>
 | ||
|  | #include <boost/type_traits/is_floating_point.hpp> 
 | ||
|  | #include <boost/math/special_functions/next.hpp> // for float_distance
 | ||
|  | 
 | ||
|  | //[numeric_derivative_example
 | ||
|  | /*`The following example shows how multiprecision calculations can be used to
 | ||
|  | obtain full precision in a numerical derivative calculation that suffers from precision loss. | ||
|  | 
 | ||
|  | Consider some well-known central difference rules for numerically | ||
|  | computing the 1st derivative of a function [f'(x)] with [/x] real. | ||
|  | 
 | ||
|  | Need a reference here?  Introduction to Partial Differential Equations, Peter J. Olver | ||
|  |  December 16, 2012  | ||
|  | 
 | ||
|  | Here, the implementation uses a C++ template that can be instantiated with various | ||
|  | floating-point types such as `float`, `double`, `long double`, or even | ||
|  | a user-defined floating-point type like __multiprecision. | ||
|  | 
 | ||
|  | We will now use the derivative template with the built-in type `double` in | ||
|  | order to numerically compute the derivative of a function, and then repeat | ||
|  | with a 5 decimal digit higher precision user-defined floating-point type.  | ||
|  | 
 | ||
|  | Consider the function  shown below. | ||
|  | !! | ||
|  | (3) | ||
|  | We will now take the derivative of this function with respect to x evaluated | ||
|  | at x = 3= 2. In other words, | ||
|  | 
 | ||
|  | (4) | ||
|  | 
 | ||
|  | The expected result is | ||
|  | 
 | ||
|  |  0:74535 59924 99929 89880 . (5) | ||
|  | The program below uses the derivative template in order to perform | ||
|  | the numerical calculation of this derivative. The program also compares the | ||
|  | numerically-obtained result with the expected result and reports the absolute | ||
|  | relative error scaled to a deviation that can easily be related to the number of | ||
|  | bits of lost precision. | ||
|  | 
 | ||
|  | */ | ||
|  | 
 | ||
|  | /*` [note Rquires the C++11 feature of
 | ||
|  | [@http://en.wikipedia.org/wiki/Anonymous_function#C.2B.2B anonymous functions]
 | ||
|  | for the derivative function calls like `[]( const double & x_) -> double`. | ||
|  | */ | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | template <typename value_type,  typename function_type> | ||
|  | value_type derivative (const value_type x, const value_type dx, function_type function) | ||
|  | { | ||
|  |   /*! \brief Compute the derivative of function using a 3-point central difference rule of O(dx^6).
 | ||
|  |     \tparam value_type, floating-point type, for example: `double` or `cpp_dec_float_50` | ||
|  |     \tparam function_type   | ||
|  |      | ||
|  |     \param x Value at which to evaluate derivative. | ||
|  |     \param dx Incremental step-size. | ||
|  |     \param function Function whose derivative is to computed. | ||
|  |    | ||
|  |     \return derivative at x. | ||
|  |   */ | ||
|  | 
 | ||
|  |   BOOST_STATIC_ASSERT_MSG(false == std::numeric_limits<value_type>::is_integer, "value_type must be a floating-point type!"); | ||
|  | 
 | ||
|  |   const value_type dx2(dx * 2U); | ||
|  |   const value_type dx3(dx * 3U); | ||
|  |   // Difference terms.
 | ||
|  |   const value_type m1 ((function (x + dx) - function(x - dx)) / 2U); | ||
|  |   const value_type m2 ((function (x + dx2) - function(x - dx2)) / 4U); | ||
|  |   const value_type m3 ((function (x + dx3) - function(x - dx3)) / 6U); | ||
|  |   const value_type fifteen_m1 (m1 * 15U); | ||
|  |   const value_type six_m2 (m2 * 6U); | ||
|  |   const value_type ten_dx (dx * 10U); | ||
|  |   return ((fifteen_m1 - six_m2) + m3) / ten_dx;  // Derivative.
 | ||
|  | } // 
 | ||
|  | 
 | ||
|  | #include <boost/multiprecision/cpp_dec_float.hpp>
 | ||
|  |   using boost::multiprecision::number; | ||
|  |   using boost::multiprecision::cpp_dec_float; | ||
|  | 
 | ||
|  | // Re-compute using 5 extra decimal digits precision (22) than double (17).
 | ||
|  | #define MP_DIGITS10 unsigned (std::numeric_limits<double>::max_digits10 + 5)
 | ||
|  | 
 | ||
|  | typedef cpp_dec_float<MP_DIGITS10> mp_backend; | ||
|  | typedef number<mp_backend> mp_type; | ||
|  | 
 | ||
|  | 
 | ||
|  | int main() | ||
|  | { | ||
|  |   { | ||
|  |     const double d = | ||
|  |       derivative | ||
|  |       ( 1.5, // x = 3.2
 | ||
|  |         std::ldexp (1., -9), // step size 2^-9 = see below for choice.
 | ||
|  |         [](const double & x)->double // Function f(x).
 | ||
|  |         { | ||
|  |           return std::sqrt((x * x) - 1.) - std::acos(1. / x); | ||
|  |         } | ||
|  |       ); | ||
|  |    | ||
|  |     // The 'exactly right' result is [sqrt]5 / 3 = 0.74535599249992989880.
 | ||
|  |     const double rel_error = (d - 0.74535599249992989880) / 0.74535599249992989880; | ||
|  |     const double bit_error = std::abs(rel_error) / std::numeric_limits<double>::epsilon(); | ||
|  |     std::cout.precision (std::numeric_limits<double>::digits10); // Show all guaranteed decimal digits.
 | ||
|  |     std::cout << std::showpoint ; // Ensure that any trailing zeros are shown too.
 | ||
|  | 
 | ||
|  |     std::cout << " derivative : " << d << std::endl; | ||
|  |     std::cout << " expected   : " << 0.74535599249992989880 << std::endl; | ||
|  |     // Can compute an 'exact' value using multiprecision type.
 | ||
|  |     std::cout << " expected   : " << sqrt(static_cast<mp_type>(5))/3U << std::endl; | ||
|  |     std::cout << " bit_error : " << static_cast<unsigned long>(bit_error)  << std::endl; | ||
|  | 
 | ||
|  |     std::cout.precision(6); | ||
|  |     std::cout << "float_distance = " << boost::math::float_distance(0.74535599249992989880, d) << std::endl; | ||
|  | 
 | ||
|  |   } | ||
|  | 
 | ||
|  |   { // Compute using multiprecision type with an extra 5 decimal digits of precision.
 | ||
|  |     const mp_type mp = | ||
|  |       derivative(mp_type(mp_type(3) / 2U), // x = 3/2
 | ||
|  |         mp_type(mp_type(1) / 10000000U), // Step size 10^7.
 | ||
|  |         [](const mp_type & x)->mp_type | ||
|  |         { | ||
|  |           return sqrt((x * x) - 1.) - acos (1. / x); // Function
 | ||
|  |         } | ||
|  |     ); | ||
|  | 
 | ||
|  |     const double d = mp.convert_to<double>(); // Convert to closest double.
 | ||
|  |     const double rel_error = (d - 0.74535599249992989880) / 0.74535599249992989880; | ||
|  |     const double bit_error = std::abs (rel_error) / std::numeric_limits<double>::epsilon(); | ||
|  |     std::cout.precision (std::numeric_limits <double>::digits10); // All guaranteed decimal digits.
 | ||
|  |     std::cout << std::showpoint ; // Ensure that any trailing zeros are shown too.
 | ||
|  |     std::cout << " derivative : " << d << std::endl; | ||
|  |     // Can compute an 'exact' value using multiprecision type.
 | ||
|  |     std::cout << " expected   : " << sqrt(static_cast<mp_type>(5))/3U << std::endl; | ||
|  |     std::cout << " expected   : " << 0.74535599249992989880 | ||
|  |     << std::endl; | ||
|  |     std::cout << " bit_error : "  << static_cast<unsigned long>(bit_error)  << std::endl; | ||
|  | 
 | ||
|  |     std::cout.precision(6); | ||
|  |     std::cout << "float_distance = " << boost::math::float_distance(0.74535599249992989880, d) << std::endl; | ||
|  | 
 | ||
|  |      | ||
|  |   } | ||
|  | 
 | ||
|  | 
 | ||
|  | } // int main()
 | ||
|  | 
 | ||
|  | /*`
 | ||
|  | The result of this program on a system with an eight-byte, 64-bit IEEE-754 | ||
|  | conforming floating-point representation for `double` is: | ||
|  | 
 | ||
|  |  derivative : 0.745355992499951 | ||
|  | 
 | ||
|  |  derivative : 0.745355992499943 | ||
|  |  expected   : 0.74535599249993 | ||
|  |  bit_error : 78 | ||
|  | 
 | ||
|  |     derivative : 0.745355992499930 | ||
|  |    expected   : 0.745355992499930 | ||
|  |    bit_error : 0 | ||
|  | 
 | ||
|  | The resulting bit error is 0. This means that the result of the derivative | ||
|  | calculation is bit-identical with the double representation of the expected result, | ||
|  | and this is the best result possible for the built-in type. | ||
|  | 
 | ||
|  | The derivative in this example has a known closed form. There are, however, | ||
|  | countless situations in numerical analysis (and not only for numerical deriva- | ||
|  | tives) for which the calculation at hand does not have a known closed-form | ||
|  | solution or for which the closed-form solution is highly inconvenient to use. In | ||
|  | such cases, this technique may be useful. | ||
|  | 
 | ||
|  | This example has shown how multiprecision can be used to add extra digits | ||
|  | to an ill-conditioned calculation that suffers from precision loss. When the result | ||
|  | of the multiprecision calculation is converted to a built-in type such as double, | ||
|  | the entire precision of the result in double is preserved. | ||
|  | 
 | ||
|  |  */ | ||
|  | 
 | ||
|  | /*
 | ||
|  | 
 | ||
|  |   Description: Autorun "J:\Cpp\big_number\Debug\numerical_derivative_example.exe" | ||
|  |    derivative : 0.745355992499943 | ||
|  |    expected   : 0.745355992499930 | ||
|  |    expected   : 0.745355992499930 | ||
|  |    bit_error : 78 | ||
|  |   float_distance = 117.000 | ||
|  |    derivative : 0.745355992499930 | ||
|  |    expected   : 0.745355992499930 | ||
|  |    expected   : 0.745355992499930 | ||
|  |    bit_error : 0 | ||
|  |   float_distance = 0.000000 | ||
|  | 
 | ||
|  |  */ | ||
|  | 
 |