root_n_finding_algorithms.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870
  1. // Copyright Paul A. Bristow 2015
  2. // Use, modification and distribution are subject to the
  3. // Boost Software License, Version 1.0.
  4. // (See accompanying file LICENSE_1_0.txt
  5. // or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. // Comparison of finding roots using TOMS748, Newton-Raphson, Halley & Schroder algorithms.
  7. // root_n_finding_algorithms.cpp Generalised for nth root version.
  8. // http://en.wikipedia.org/wiki/Cube_root
  9. // Note that this file contains Quickbook mark-up as well as code
  10. // and comments, don't change any of the special comment mark-ups!
  11. // This program also writes files in Quickbook tables mark-up format.
  12. #include <boost/cstdlib.hpp>
  13. #include <boost/config.hpp>
  14. #include <boost/array.hpp>
  15. #include <boost/type_traits/is_floating_point.hpp>
  16. #include <boost/math/concepts/real_concept.hpp>
  17. #include <boost/math/tools/roots.hpp>
  18. //using boost::math::policies::policy;
  19. //using boost::math::tools::eps_tolerance; // Binary functor for specified number of bits.
  20. //using boost::math::tools::bracket_and_solve_root;
  21. //using boost::math::tools::toms748_solve;
  22. //using boost::math::tools::halley_iterate;
  23. //using boost::math::tools::newton_raphson_iterate;
  24. //using boost::math::tools::schroder_iterate;
  25. #include <boost/math/special_functions/next.hpp> // For float_distance.
  26. #include <boost/math/special_functions/pow.hpp> // For pow<N>.
  27. #include <boost/math/tools/tuple.hpp> // for tuple and make_tuple.
  28. #include <boost/multiprecision/cpp_bin_float.hpp> // is binary.
  29. using boost::multiprecision::cpp_bin_float_100;
  30. using boost::multiprecision::cpp_bin_float_50;
  31. #include <boost/timer/timer.hpp>
  32. #include <boost/system/error_code.hpp>
  33. #include <boost/preprocessor/stringize.hpp>
  34. // STL
  35. #include <iostream>
  36. #include <iomanip>
  37. #include <string>
  38. #include <vector>
  39. #include <limits>
  40. #include <fstream> // std::ofstream
  41. #include <cmath>
  42. #include <typeinfo> // for type name using typid(thingy).name();
  43. #ifdef __FILE__
  44. std::string sourcefilename = __FILE__;
  45. #else
  46. std::string sourcefilename("");
  47. #endif
  48. std::string chop_last(std::string s)
  49. {
  50. std::string::size_type pos = s.find_last_of("\\/");
  51. if(pos != std::string::npos)
  52. s.erase(pos);
  53. else if(s.empty())
  54. abort();
  55. else
  56. s.erase();
  57. return s;
  58. }
  59. std::string make_root()
  60. {
  61. std::string result;
  62. if(sourcefilename.find_first_of(":") != std::string::npos)
  63. {
  64. result = chop_last(sourcefilename); // lose filename part
  65. result = chop_last(result); // lose /example/
  66. result = chop_last(result); // lose /math/
  67. result = chop_last(result); // lose /libs/
  68. }
  69. else
  70. {
  71. result = chop_last(sourcefilename); // lose filename part
  72. if(result.empty())
  73. result = ".";
  74. result += "/../../..";
  75. }
  76. return result;
  77. }
  78. std::string short_file_name(std::string s)
  79. {
  80. std::string::size_type pos = s.find_last_of("\\/");
  81. if(pos != std::string::npos)
  82. s.erase(0, pos + 1);
  83. return s;
  84. }
  85. std::string boost_root = make_root();
  86. std::string fp_hardware; // Any hardware features like SEE or AVX
  87. const std::string roots_name = "libs/math/doc/roots/";
  88. const std::string full_roots_name(boost_root + "/libs/math/doc/roots/");
  89. const std::size_t nooftypes = 4;
  90. const std::size_t noofalgos = 4;
  91. double digits_accuracy = 1.0; // 1 == maximum possible accuracy.
  92. std::stringstream ss;
  93. std::ofstream fout;
  94. std::vector<std::string> algo_names =
  95. {
  96. "TOMS748", "Newton", "Halley", "Schr'''&#xf6;'''der"
  97. };
  98. std::vector<std::string> names =
  99. {
  100. "float", "double", "long double", "cpp_bin_float50"
  101. };
  102. uintmax_t iters; // Global as value of iterations is not returned.
  103. struct root_info
  104. { // for a floating-point type, float, double ...
  105. std::size_t max_digits10; // for type.
  106. std::string full_typename; // for type from type_id.name().
  107. std::string short_typename; // for type "float", "double", "cpp_bin_float_50" ....
  108. std::size_t bin_digits; // binary in floating-point type numeric_limits<T>::digits;
  109. int get_digits; // fraction of maximum possible accuracy required.
  110. // = digits * digits_accuracy
  111. // Vector of values (4) for each algorithm, TOMS748, Newton, Halley & Schroder.
  112. //std::vector< boost::int_least64_t> times; converted to int.
  113. std::vector<int> times; // arbirary units (ticks).
  114. //boost::int_least64_t min_time = std::numeric_limits<boost::int_least64_t>::max(); // Used to normalize times (as int).
  115. std::vector<double> normed_times;
  116. int min_time = (std::numeric_limits<int>::max)(); // Used to normalize times.
  117. std::vector<uintmax_t> iterations;
  118. std::vector<long int> distances;
  119. std::vector<cpp_bin_float_100> full_results;
  120. }; // struct root_info
  121. std::vector<root_info> root_infos; // One element for each floating-point type used.
  122. inline std::string build_test_name(const char* type_name, const char* test_name)
  123. {
  124. std::string result(BOOST_COMPILER);
  125. result += "|";
  126. result += BOOST_STDLIB;
  127. result += "|";
  128. result += BOOST_PLATFORM;
  129. result += "|";
  130. result += type_name;
  131. result += "|";
  132. result += test_name;
  133. #if defined(_DEBUG) || !defined(NDEBUG)
  134. result += "|";
  135. result += " debug";
  136. #else
  137. result += "|";
  138. result += " release";
  139. #endif
  140. result += "|";
  141. return result;
  142. } // std::string build_test_name
  143. // Algorithms //////////////////////////////////////////////
  144. // No derivatives - using TOMS748 internally.
  145. template <int N, typename T = double>
  146. struct nth_root_functor_noderiv
  147. { // Nth root of x using only function - no derivatives.
  148. nth_root_functor_noderiv(T const& to_find_root_of) : a(to_find_root_of)
  149. { // Constructor just stores value a to find root of.
  150. }
  151. T operator()(T const& x)
  152. {
  153. using boost::math::pow;
  154. T fx = pow<N>(x) -a; // Difference (estimate x^n - a).
  155. return fx;
  156. }
  157. private:
  158. T a; // to be 'cube_rooted'.
  159. }; // template <int N, class T> struct nth_root_functor_noderiv
  160. template <int N, class T = double>
  161. T nth_root_noderiv(T x)
  162. { // return Nth root of x using bracket_and_solve (using NO derivatives).
  163. using namespace std; // Help ADL of std functions.
  164. using namespace boost::math::tools; // For bracket_and_solve_root.
  165. typedef double guess_type;
  166. int exponent;
  167. frexp(static_cast<guess_type>(x), &exponent); // Get exponent of z (ignore mantissa).
  168. T guess = static_cast<T>(ldexp(static_cast<guess_type>(1.), exponent / N)); // Rough guess is to divide the exponent by n.
  169. //T min = static_cast<T>(ldexp(static_cast<guess_type>(1.) / 2, exponent / N)); // Minimum possible value is half our guess.
  170. //T max = static_cast<T>(ldexp(static_cast<guess_type>(2.), exponent / N)); // Maximum possible value is twice our guess.
  171. T factor = 2; // How big steps to take when searching.
  172. const boost::uintmax_t maxit = 50; // Limit to maximum iterations.
  173. boost::uintmax_t it = maxit; // Initally our chosen max iterations, but updated with actual.
  174. bool is_rising = true; // So if result if guess^3 is too low, then try increasing guess.
  175. // Some fraction of digits is used to control how accurate to try to make the result.
  176. int get_digits = std::numeric_limits<T>::digits - 2;
  177. eps_tolerance<T> tol(get_digits); // Set the tolerance.
  178. std::pair<T, T> r;
  179. r = bracket_and_solve_root(nth_root_functor_noderiv<N, T>(x), guess, factor, is_rising, tol, it);
  180. iters = it;
  181. T result = r.first + (r.second - r.first) / 2; // Midway between brackets.
  182. return result;
  183. } // template <class T> T nth_root_noderiv(T x)
  184. // Using 1st derivative only Newton-Raphson
  185. template <int N, class T = double>
  186. struct nth_root_functor_1deriv
  187. { // Functor also returning 1st derviative.
  188. BOOST_STATIC_ASSERT_MSG(boost::is_integral<T>::value == false, "Only floating-point type types can be used!");
  189. BOOST_STATIC_ASSERT_MSG((N > 0) == true, "root N must be > 0!");
  190. nth_root_functor_1deriv(T const& to_find_root_of) : a(to_find_root_of)
  191. { // Constructor stores value a to find root of, for example:
  192. }
  193. std::pair<T, T> operator()(T const& x)
  194. { // Return both f(x) and f'(x).
  195. using boost::math::pow; // // Compile-time integral power.
  196. T p = pow<N - 1>(x);
  197. return std::make_pair(p * x - a, N * p); // 'return' both fx and dx.
  198. }
  199. private:
  200. T a; // to be 'nth_rooted'.
  201. }; // struct nthroot__functor_1deriv
  202. template <int N, class T = double>
  203. T nth_root_1deriv(T x)
  204. { // return nth root of x using 1st derivative and Newton_Raphson.
  205. using namespace std; // Help ADL of std functions.
  206. using namespace boost::math::tools; // For newton_raphson_iterate.
  207. BOOST_STATIC_ASSERT_MSG(boost::is_integral<T>::value == false, "Only floating-point type types can be used!");
  208. BOOST_STATIC_ASSERT_MSG((N > 0) == true, "root N must be > 0!");
  209. BOOST_STATIC_ASSERT_MSG((N > 1000) == false, "root N is too big!");
  210. typedef double guess_type;
  211. int exponent;
  212. frexp(static_cast<guess_type>(x), &exponent); // Get exponent of z (ignore mantissa).
  213. T guess = static_cast<T>(ldexp(static_cast<guess_type>(1.), exponent / N)); // Rough guess is to divide the exponent by n.
  214. T min = static_cast<T>(ldexp(static_cast<guess_type>(1.) / 2, exponent / N)); // Minimum possible value is half our guess.
  215. T max = static_cast<T>(ldexp(static_cast<guess_type>(2.), exponent / N)); // Maximum possible value is twice our guess.
  216. int digits = std::numeric_limits<T>::digits; // Maximum possible binary digits accuracy for type T.
  217. int get_digits = static_cast<int>(digits * 0.6);
  218. const boost::uintmax_t maxit = 20;
  219. boost::uintmax_t it = maxit;
  220. T result = newton_raphson_iterate(nth_root_functor_1deriv<N, T>(x), guess, min, max, get_digits, it);
  221. iters = it;
  222. return result;
  223. } // T nth_root_1_deriv Newton-Raphson
  224. // Using 1st and 2nd derivatives with Halley algorithm.
  225. template <int N, class T = double>
  226. struct nth_root_functor_2deriv
  227. { // Functor returning both 1st and 2nd derivatives.
  228. BOOST_STATIC_ASSERT_MSG(boost::is_integral<T>::value == false, "Only floating-point type types can be used!");
  229. BOOST_STATIC_ASSERT_MSG((N > 0) == true, "root N must be > 0!");
  230. nth_root_functor_2deriv(T const& to_find_root_of) : a(to_find_root_of)
  231. { // Constructor stores value a to find root of, for example:
  232. }
  233. // using boost::math::tuple; // to return three values.
  234. std::tuple<T, T, T> operator()(T const& x)
  235. { // Return f(x), f'(x) and f''(x).
  236. using boost::math::pow; // Compile-time integral power.
  237. T p = pow<N - 2>(x);
  238. return std::make_tuple(p * x * x - a, p * x * N, p * N * (N - 1)); // 'return' fx, dx and d2x.
  239. }
  240. private:
  241. T a; // to be 'nth_rooted'.
  242. };
  243. template <int N, class T = double>
  244. T nth_root_2deriv(T x)
  245. { // return nth root of x using 1st and 2nd derivatives and Halley.
  246. using namespace std; // Help ADL of std functions.
  247. using namespace boost::math::tools; // For halley_iterate.
  248. BOOST_STATIC_ASSERT_MSG(boost::is_integral<T>::value == false, "Only floating-point type types can be used!");
  249. BOOST_STATIC_ASSERT_MSG((N > 0) == true, "root N must be > 0!");
  250. BOOST_STATIC_ASSERT_MSG((N > 1000) == false, "root N is too big!");
  251. typedef double guess_type;
  252. int exponent;
  253. frexp(static_cast<guess_type>(x), &exponent); // Get exponent of z (ignore mantissa).
  254. T guess = static_cast<T>(ldexp(static_cast<guess_type>(1.), exponent / N)); // Rough guess is to divide the exponent by n.
  255. T min = static_cast<T>(ldexp(static_cast<guess_type>(1.) / 2, exponent / N)); // Minimum possible value is half our guess.
  256. T max = static_cast<T>(ldexp(static_cast<guess_type>(2.), exponent / N)); // Maximum possible value is twice our guess.
  257. int digits = std::numeric_limits<T>::digits; // Maximum possible binary digits accuracy for type T.
  258. int get_digits = static_cast<int>(digits * 0.4);
  259. const boost::uintmax_t maxit = 20;
  260. boost::uintmax_t it = maxit;
  261. T result = halley_iterate(nth_root_functor_2deriv<N, T>(x), guess, min, max, get_digits, it);
  262. iters = it;
  263. return result;
  264. } // nth_2deriv Halley
  265. template <int N, class T = double>
  266. T nth_root_2deriv_s(T x)
  267. { // return nth root of x using 1st and 2nd derivatives and Schroder.
  268. using namespace std; // Help ADL of std functions.
  269. using namespace boost::math::tools; // For schroder_iterate.
  270. BOOST_STATIC_ASSERT_MSG(boost::is_integral<T>::value == false, "Only floating-point type types can be used!");
  271. BOOST_STATIC_ASSERT_MSG((N > 0) == true, "root N must be > 0!");
  272. BOOST_STATIC_ASSERT_MSG((N > 1000) == false, "root N is too big!");
  273. typedef double guess_type;
  274. int exponent;
  275. frexp(static_cast<guess_type>(x), &exponent); // Get exponent of z (ignore mantissa).
  276. T guess = static_cast<T>(ldexp(static_cast<guess_type>(1.), exponent / N)); // Rough guess is to divide the exponent by n.
  277. T min = static_cast<T>(ldexp(static_cast<guess_type>(1.) / 2, exponent / N)); // Minimum possible value is half our guess.
  278. T max = static_cast<T>(ldexp(static_cast<guess_type>(2.), exponent / N)); // Maximum possible value is twice our guess.
  279. int get_digits = static_cast<int>(std::numeric_limits<T>::digits * 0.4);
  280. const boost::uintmax_t maxit = 20;
  281. boost::uintmax_t it = maxit;
  282. T result = schroder_iterate(nth_root_functor_2deriv<N, T>(x), guess, min, max, get_digits, it);
  283. iters = it;
  284. return result;
  285. } // T nth_root_2deriv_s Schroder
  286. //////////////////////////////////////////////////////// end of algorithms - perhaps in a separate .hpp?
  287. //! Print 4 floating-point types info: max_digits10, digits and required accuracy digits as a Quickbook table.
  288. int table_type_info(double digits_accuracy)
  289. {
  290. std::string qbk_name = full_roots_name; // Prefix by boost_root file.
  291. qbk_name += "type_info_table";
  292. std::stringstream ss;
  293. ss.precision(3);
  294. ss << "_" << digits_accuracy * 100;
  295. qbk_name += ss.str();
  296. #ifdef _MSC_VER
  297. qbk_name += "_msvc.qbk";
  298. #else // assume GCC
  299. qbk_name += "_gcc.qbk";
  300. #endif
  301. // Example: type_info_table_100_msvc.qbk
  302. fout.open(qbk_name, std::ios_base::out);
  303. if (fout.is_open())
  304. {
  305. std::cout << "Output type table to " << qbk_name << std::endl;
  306. }
  307. else
  308. { // Failed to open.
  309. std::cout << " Open file " << qbk_name << " for output failed!" << std::endl;
  310. std::cout << "errno " << errno << std::endl;
  311. return errno;
  312. }
  313. fout <<
  314. "[/"
  315. << qbk_name
  316. << "\n"
  317. "Copyright 2015 Paul A. Bristow.""\n"
  318. "Copyright 2015 John Maddock.""\n"
  319. "Distributed under the Boost Software License, Version 1.0.""\n"
  320. "(See accompanying file LICENSE_1_0.txt or copy at""\n"
  321. "http://www.boost.org/LICENSE_1_0.txt).""\n"
  322. "]""\n"
  323. << std::endl;
  324. fout << "[h6 Fraction of maximum possible bits of accuracy required is " << digits_accuracy << ".]\n" << std::endl;
  325. std::string table_id("type_info");
  326. table_id += ss.str(); // Fraction digits accuracy.
  327. #ifdef _MSC_VER
  328. table_id += "_msvc";
  329. #else // assume GCC
  330. table_id += "_gcc";
  331. #endif
  332. fout << "[table:" << table_id << " Digits for float, double, long double and cpp_bin_float_50\n"
  333. << "[[type name] [max_digits10] [binary digits] [required digits]]\n";// header.
  334. // For all fout types:
  335. fout << "[[" << "float" << "]"
  336. << "[" << std::numeric_limits<float>::max_digits10 << "]" // max_digits10
  337. << "[" << std::numeric_limits<float>::digits << "]"// < "Binary digits
  338. << "[" << static_cast<int>(std::numeric_limits<float>::digits * digits_accuracy) << "]]\n"; // Accuracy digits.
  339. fout << "[[" << "float" << "]"
  340. << "[" << std::numeric_limits<double>::max_digits10 << "]" // max_digits10
  341. << "[" << std::numeric_limits<double>::digits << "]"// < "Binary digits
  342. << "[" << static_cast<int>(std::numeric_limits<double>::digits * digits_accuracy) << "]]\n"; // Accuracy digits.
  343. fout << "[[" << "long double" << "]"
  344. << "[" << std::numeric_limits<long double>::max_digits10 << "]" // max_digits10
  345. << "[" << std::numeric_limits<long double>::digits << "]"// < "Binary digits
  346. << "[" << static_cast<int>(std::numeric_limits<long double>::digits * digits_accuracy) << "]]\n"; // Accuracy digits.
  347. fout << "[[" << "cpp_bin_float_50" << "]"
  348. << "[" << std::numeric_limits<cpp_bin_float_50>::max_digits10 << "]" // max_digits10
  349. << "[" << std::numeric_limits<cpp_bin_float_50>::digits << "]"// < "Binary digits
  350. << "[" << static_cast<int>(std::numeric_limits<cpp_bin_float_50>::digits * digits_accuracy) << "]]\n"; // Accuracy digits.
  351. fout << "] [/table table_id_msvc] \n" << std::endl; // End of table.
  352. fout.close();
  353. return 0;
  354. } // type_table
  355. //! Evaluate root N timing for each algorithm, and for one floating-point type T.
  356. template <int N, typename T>
  357. int test_root(cpp_bin_float_100 big_value, cpp_bin_float_100 answer, const char* type_name, std::size_t type_no)
  358. {
  359. std::size_t max_digits = 2 + std::numeric_limits<T>::digits * 3010 / 10000;
  360. // For new versions use max_digits10
  361. // std::cout.precision(std::numeric_limits<T>::max_digits10);
  362. std::cout.precision(max_digits);
  363. std::cout << std::showpoint << std::endl; // Show trailing zeros too.
  364. root_infos.push_back(root_info());
  365. root_infos[type_no].max_digits10 = max_digits;
  366. root_infos[type_no].full_typename = typeid(T).name(); // Full typename.
  367. root_infos[type_no].short_typename = type_name; // Short typename.
  368. root_infos[type_no].bin_digits = std::numeric_limits<T>::digits;
  369. root_infos[type_no].get_digits = static_cast<int>(std::numeric_limits<T>::digits * digits_accuracy);
  370. T to_root = static_cast<T>(big_value);
  371. T result; // root
  372. T sum = 0;
  373. T ans = static_cast<T>(answer);
  374. using boost::timer::nanosecond_type;
  375. using boost::timer::cpu_times;
  376. using boost::timer::cpu_timer;
  377. int eval_count = boost::is_floating_point<T>::value ? 10000000 : 100000; // To give a sufficiently stable timing for the fast built-in types,
  378. //int eval_count = 1000000; // To give a sufficiently stable timing for the fast built-in types,
  379. // This takes an inconveniently long time for multiprecision cpp_bin_float_50 etc types.
  380. cpu_times now; // Holds wall, user and system times.
  381. { // Evaluate times etc for each algorithm.
  382. //algorithm_names.push_back("TOMS748"); //
  383. cpu_timer ti; // Can start, pause, resume and stop, and read elapsed.
  384. ti.start();
  385. for (long i = 0; i < eval_count; ++i)
  386. {
  387. result = nth_root_noderiv<N, T>(to_root); //
  388. sum += result;
  389. }
  390. now = ti.elapsed();
  391. int time = static_cast<int>(now.user / eval_count);
  392. root_infos[type_no].times.push_back(time); // CPU time taken.
  393. if (time < root_infos[type_no].min_time)
  394. {
  395. root_infos[type_no].min_time = time;
  396. }
  397. ti.stop();
  398. long int distance = static_cast<int>(boost::math::float_distance<T>(result, ans));
  399. root_infos[type_no].distances.push_back(distance);
  400. root_infos[type_no].iterations.push_back(iters); //
  401. root_infos[type_no].full_results.push_back(result);
  402. }
  403. {
  404. // algorithm_names.push_back("Newton"); // algorithm
  405. cpu_timer ti; // Can start, pause, resume and stop, and read elapsed.
  406. ti.start();
  407. for (long i = 0; i < eval_count; ++i)
  408. {
  409. result = nth_root_1deriv<N, T>(to_root); //
  410. sum += result;
  411. }
  412. now = ti.elapsed();
  413. int time = static_cast<int>(now.user / eval_count);
  414. root_infos[type_no].times.push_back(time); // CPU time taken.
  415. if (time < root_infos[type_no].min_time)
  416. {
  417. root_infos[type_no].min_time = time;
  418. }
  419. ti.stop();
  420. long int distance = static_cast<int>(boost::math::float_distance<T>(result, ans));
  421. root_infos[type_no].distances.push_back(distance);
  422. root_infos[type_no].iterations.push_back(iters); //
  423. root_infos[type_no].full_results.push_back(result);
  424. }
  425. {
  426. //algorithm_names.push_back("Halley"); // algorithm
  427. cpu_timer ti; // Can start, pause, resume and stop, and read elapsed.
  428. ti.start();
  429. for (long i = 0; i < eval_count; ++i)
  430. {
  431. result = nth_root_2deriv<N>(to_root); //
  432. sum += result;
  433. }
  434. now = ti.elapsed();
  435. int time = static_cast<int>(now.user / eval_count);
  436. root_infos[type_no].times.push_back(time); // CPU time taken.
  437. ti.stop();
  438. if (time < root_infos[type_no].min_time)
  439. {
  440. root_infos[type_no].min_time = time;
  441. }
  442. long int distance = static_cast<int>(boost::math::float_distance<T>(result, ans));
  443. root_infos[type_no].distances.push_back(distance);
  444. root_infos[type_no].iterations.push_back(iters); //
  445. root_infos[type_no].full_results.push_back(result);
  446. }
  447. {
  448. // algorithm_names.push_back("Schroder"); // algorithm
  449. cpu_timer ti; // Can start, pause, resume and stop, and read elapsed.
  450. ti.start();
  451. for (long i = 0; i < eval_count; ++i)
  452. {
  453. result = nth_root_2deriv_s<N>(to_root); //
  454. sum += result;
  455. }
  456. now = ti.elapsed();
  457. int time = static_cast<int>(now.user / eval_count);
  458. root_infos[type_no].times.push_back(time); // CPU time taken.
  459. if (time < root_infos[type_no].min_time)
  460. {
  461. root_infos[type_no].min_time = time;
  462. }
  463. ti.stop();
  464. long int distance = static_cast<int>(boost::math::float_distance<T>(result, ans));
  465. root_infos[type_no].distances.push_back(distance);
  466. root_infos[type_no].iterations.push_back(iters); //
  467. root_infos[type_no].full_results.push_back(result);
  468. }
  469. for (size_t i = 0; i != root_infos[type_no].times.size(); i++) // For each time.
  470. { // Normalize times.
  471. root_infos[type_no].normed_times.push_back(static_cast<double>(root_infos[type_no].times[i]) / root_infos[type_no].min_time);
  472. }
  473. std::cout << "Accumulated result was: " << sum << std::endl;
  474. return 4; // eval_count of how many algorithms used.
  475. } // test_root
  476. /*! Fill array of times, interations, etc for Nth root for all 4 types,
  477. and write a table of results in Quickbook format.
  478. */
  479. template <int N>
  480. void table_root_info(cpp_bin_float_100 full_value)
  481. {
  482. using std::abs;
  483. std::cout << nooftypes << " floating-point types tested:" << std::endl;
  484. #if defined(_DEBUG) || !defined(NDEBUG)
  485. std::cout << "Compiled in debug mode." << std::endl;
  486. #else
  487. std::cout << "Compiled in optimise mode." << std::endl;
  488. #endif
  489. std::cout << "FP hardware " << fp_hardware << std::endl;
  490. // Compute the 'right' answer for root N at 100 decimal digits.
  491. cpp_bin_float_100 full_answer = nth_root_noderiv<N, cpp_bin_float_100>(full_value);
  492. root_infos.clear(); // Erase any previous data.
  493. // Fill the elements of the array for each floating-point type.
  494. test_root<N, float>(full_value, full_answer, "float", 0);
  495. test_root<N, double>(full_value, full_answer, "double", 1);
  496. test_root<N, long double>(full_value, full_answer, "long double", 2);
  497. test_root<N, cpp_bin_float_50>(full_value, full_answer, "cpp_bin_float_50", 3);
  498. // Use info from 4 floating point types to
  499. // Prepare Quickbook table for a single root
  500. // with columns of times, iterations, distances repeated for various floating-point types,
  501. // and 4 rows for each algorithm.
  502. std::stringstream table_info;
  503. table_info.precision(3);
  504. table_info << "[table:root_" << N << " " << N << "th root(" << static_cast<float>(full_value) << ") for float, double, long double and cpp_bin_float_50 types";
  505. if (fp_hardware != "")
  506. {
  507. table_info << ", using " << fp_hardware;
  508. }
  509. table_info << std::endl;
  510. fout << table_info.str()
  511. << "[[][float][][][] [][double][][][] [][long d][][][] [][cpp50][][]]\n"
  512. << "[[Algo ]";
  513. for (size_t tp = 0; tp != nooftypes; tp++)
  514. { // For all types:
  515. fout << "[Its]" << "[Times]" << "[Norm]" << "[Dis]" << "[ ]";
  516. }
  517. fout << "]" << std::endl;
  518. // Row for all algorithms.
  519. for (std::size_t algo = 0; algo != noofalgos; algo++)
  520. {
  521. fout << "[[" << std::left << std::setw(9) << algo_names[algo] << "]";
  522. for (size_t tp = 0; tp != nooftypes; tp++)
  523. { // For all types:
  524. fout
  525. << "[" << std::right << std::showpoint
  526. << std::setw(3) << std::setprecision(2) << root_infos[tp].iterations[algo] << "]["
  527. << std::setw(5) << std::setprecision(5) << root_infos[tp].times[algo] << "][";
  528. fout << std::setw(3) << std::setprecision(3);
  529. double normed_time = root_infos[tp].normed_times[algo];
  530. if (abs(normed_time - 1.00) <= 0.05)
  531. { // At or near the best time, so show as blue.
  532. fout << "[role blue " << normed_time << "]";
  533. }
  534. else if (abs(normed_time) > 4.)
  535. { // markedly poor so show as red.
  536. fout << "[role red " << normed_time << "]";
  537. }
  538. else
  539. { // Not the best, so normal black.
  540. fout << normed_time;
  541. }
  542. fout << "]["
  543. << std::setw(3) << std::setprecision(2) << root_infos[tp].distances[algo] << "][ ]";
  544. } // tp
  545. fout << "]" << std::endl;
  546. } // for algo
  547. fout << "] [/end of table root]\n";
  548. } // void table_root_info
  549. /*! Output program header, table of type info, and tables for 4 algorithms and 4 floating-point types,
  550. for Nth root required digits_accuracy.
  551. */
  552. int roots_tables(cpp_bin_float_100 full_value, double digits_accuracy)
  553. {
  554. ::digits_accuracy = digits_accuracy;
  555. // Save globally so that it is available to root-finding algorithms. Ugly :-(
  556. #if defined(_DEBUG) || !defined(NDEBUG)
  557. std::string debug_or_optimize("Compiled in debug mode.");
  558. #else
  559. std::string debug_or_optimize("Compiled in optimise mode.");
  560. #endif
  561. // Create filename for roots_table
  562. std::string qbk_name = full_roots_name;
  563. qbk_name += "roots_table";
  564. std::stringstream ss;
  565. ss.precision(3);
  566. // ss << "_" << N // now put all the tables in one .qbk file?
  567. ss << "_" << digits_accuracy * 100
  568. << std::flush;
  569. // Assume only save optimize mode runs, so don't add any _DEBUG info.
  570. qbk_name += ss.str();
  571. #ifdef _MSC_VER
  572. qbk_name += "_msvc";
  573. #else // assume GCC
  574. qbk_name += "_gcc";
  575. #endif
  576. if (fp_hardware != "")
  577. {
  578. qbk_name += fp_hardware;
  579. }
  580. qbk_name += ".qbk";
  581. fout.open(qbk_name, std::ios_base::out);
  582. if (fout.is_open())
  583. {
  584. std::cout << "Output root table to " << qbk_name << std::endl;
  585. }
  586. else
  587. { // Failed to open.
  588. std::cout << " Open file " << qbk_name << " for output failed!" << std::endl;
  589. std::cout << "errno " << errno << std::endl;
  590. return errno;
  591. }
  592. fout <<
  593. "[/"
  594. << qbk_name
  595. << "\n"
  596. "Copyright 2015 Paul A. Bristow.""\n"
  597. "Copyright 2015 John Maddock.""\n"
  598. "Distributed under the Boost Software License, Version 1.0.""\n"
  599. "(See accompanying file LICENSE_1_0.txt or copy at""\n"
  600. "http://www.boost.org/LICENSE_1_0.txt).""\n"
  601. "]""\n"
  602. << std::endl;
  603. // Print out the program/compiler/stdlib/platform names as a Quickbook comment:
  604. fout << "\n[h6 Program " << sourcefilename << ",\n "
  605. << BOOST_COMPILER << ", "
  606. << BOOST_STDLIB << ", "
  607. << BOOST_PLATFORM << "\n"
  608. << debug_or_optimize
  609. << ((fp_hardware != "") ? ", " + fp_hardware : "")
  610. << "]" // [h6 close].
  611. << std::endl;
  612. fout << "Fraction of full accuracy " << digits_accuracy << std::endl;
  613. table_root_info<5>(full_value);
  614. table_root_info<7>(full_value);
  615. table_root_info<11>(full_value);
  616. fout.close();
  617. // table_type_info(digits_accuracy);
  618. return 0;
  619. } // roots_tables
  620. int main()
  621. {
  622. using namespace boost::multiprecision;
  623. using namespace boost::math;
  624. try
  625. {
  626. std::cout << "Tests run with " << BOOST_COMPILER << ", "
  627. << BOOST_STDLIB << ", " << BOOST_PLATFORM << ", ";
  628. // How to: Configure Visual C++ Projects to Target 64-Bit Platforms
  629. // https://msdn.microsoft.com/en-us/library/9yb4317s.aspx
  630. #ifdef _M_X64 // Defined for compilations that target x64 processors.
  631. std::cout << "X64 " << std::endl;
  632. fp_hardware += "_X64";
  633. #else
  634. # ifdef _M_IX86
  635. std::cout << "X32 " << std::endl;
  636. fp_hardware += "_X86";
  637. # endif
  638. #endif
  639. #ifdef _M_AMD64
  640. std::cout << "AMD64 " << std::endl;
  641. // fp_hardware += "_AMD64";
  642. #endif
  643. // https://msdn.microsoft.com/en-us/library/7t5yh4fd.aspx
  644. // /arch (x86) options /arch:[IA32|SSE|SSE2|AVX|AVX2]
  645. // default is to use SSE and SSE2 instructions by default.
  646. // https://msdn.microsoft.com/en-us/library/jj620901.aspx
  647. // /arch (x64) options /arch:AVX and /arch:AVX2
  648. // MSVC doesn't bother to set these SSE macros!
  649. // http://stackoverflow.com/questions/18563978/sse-sse2-is-enabled-control-in-visual-studio
  650. // https://msdn.microsoft.com/en-us/library/b0084kay.aspx predefined macros.
  651. // But some of these macros are *not* defined by MSVC,
  652. // unlike AVX (but *are* defined by GCC and Clang).
  653. // So the macro code above does define them.
  654. #if (defined(_M_AMD64) || defined (_M_X64))
  655. #ifndef _M_X64
  656. # define _M_X64
  657. #endif
  658. #ifndef __SSE2__
  659. # define __SSE2__
  660. #endif
  661. #else
  662. # ifdef _M_IX86_FP // Expands to an integer literal value indicating which /arch compiler option was used:
  663. std::cout << "Floating-point _M_IX86_FP = " << _M_IX86_FP << std::endl;
  664. # if (_M_IX86_FP == 2) // 2 if /arch:SSE2, /arch:AVX or /arch:AVX2
  665. # define __SSE2__ // x32
  666. # elif (_M_IX86_FP == 1) // 1 if /arch:SSE was used.
  667. # define __SSE__ // x32
  668. # elif (_M_IX86_FP == 0) // 0 if /arch:IA32 was used.
  669. # define _X32 // No special FP instructions.
  670. # endif
  671. # endif
  672. #endif
  673. // Set the fp_hardware that is used in the .qbk filename.
  674. #ifdef __AVX2__
  675. std::cout << "Floating-point AVX2 " << std::endl;
  676. fp_hardware += "_AVX2";
  677. # else
  678. # ifdef __AVX__
  679. std::cout << "Floating-point AVX " << std::endl;
  680. fp_hardware += "_AVX";
  681. # else
  682. # ifdef __SSE2__
  683. std::cout << "Floating-point SSE2 " << std::endl;
  684. fp_hardware += "_SSE2";
  685. # else
  686. # ifdef __SSE__
  687. std::cout << "Floating-point SSE " << std::endl;
  688. fp_hardware += "_SSE";
  689. # endif
  690. # endif
  691. # endif
  692. # endif
  693. #ifdef _M_IX86
  694. std::cout << "Floating-point X86 _M_IX86 = " << _M_IX86 << std::endl;
  695. // https://msdn.microsoft.com/en-us/library/aa273918%28v=vs.60%29.aspx#_predir_table_1..3
  696. // 600 = Pentium Pro
  697. #endif
  698. #ifdef _MSC_FULL_VER
  699. std::cout << "Floating-point _MSC_FULL_VER " << _MSC_FULL_VER << std::endl;
  700. #endif
  701. #ifdef __MSVC_RUNTIME_CHECKS
  702. std::cout << "Runtime __MSVC_RUNTIME_CHECKS " << std::endl;
  703. #endif
  704. BOOST_MATH_CONTROL_FP;
  705. cpp_bin_float_100 full_value("28.");
  706. // Compute full answer to more than precision of tests.
  707. //T value = 28.; // integer (exactly representable as floating-point)
  708. // whose cube root is *not* exactly representable.
  709. // Wolfram Alpha command N[28 ^ (1 / 3), 100] computes cube root to 100 decimal digits.
  710. // 3.036588971875662519420809578505669635581453977248111123242141654169177268411884961770250390838097895
  711. std::cout.precision(100);
  712. std::cout << "value " << full_value << std::endl;
  713. // std::cout << ",\n""answer = " << full_answer << std::endl;
  714. std::cout.precision(6);
  715. // cbrt cpp_bin_float_100 full_answer("3.036588971875662519420809578505669635581453977248111123242141654169177268411884961770250390838097895");
  716. // Output the table of types, maxdigits10 and digits and required digits for some accuracies.
  717. // Output tables for some roots at full accuracy.
  718. roots_tables(full_value, 1.);
  719. // Output tables for some roots at less accuracy.
  720. //roots_tables(full_value, 0.75);
  721. return boost::exit_success;
  722. }
  723. catch (std::exception const& ex)
  724. {
  725. std::cout << "exception thrown: " << ex.what() << std::endl;
  726. return boost::exit_failure;
  727. }
  728. } // int main()
  729. /*
  730. */