math.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. // TODO: need a test file per header and include the header first, to detect missing includes
  2. #define SIMPLE_SUPPORT_ALGORITHM_NUMERIC_CLEVER_REPEAT
  3. #include "simple/support/math.hpp"
  4. #include "simple/support/random.hpp"
  5. #include <cassert>
  6. #include <random>
  7. #include <iostream>
  8. #include <iomanip>
  9. #include <chrono>
  10. using namespace simple::support;
  11. using namespace std::chrono;
  12. void FloatEquality()
  13. {
  14. // ummm how exactly do i do this?
  15. auto seed = std::random_device{}();
  16. std::cout << "Float equality test seed: " << std::hex << std::showbase << seed << std::endl;
  17. random::engine::tiny<unsigned long long> random{seed};
  18. std::uniform_real_distribution<double> number{};
  19. for(int i = 0; i < 10'000'000; ++i)
  20. {
  21. double a = number(random);
  22. double b = number(random);
  23. // someting like this??
  24. assert( almost_equal(a/b * b, a) );
  25. assert( almost_equal(b/a * a, b) );
  26. assert( almost_equal(std::sqrt(a)*std::sqrt(a), a) );
  27. assert( almost_equal(std::sqrt(b)*std::sqrt(b), b) );
  28. assert( !almost_equal(a,b) ); // ummmm... well... i mean... if this fails - you win! =)
  29. };
  30. }
  31. void FloatTruncation()
  32. {
  33. auto seed = std::random_device{}();
  34. std::cout << "Float trunc test seed: " << std::hex << std::showbase << seed << std::endl;
  35. random::engine::tiny<unsigned long long> random{seed};
  36. std::uniform_real_distribution<long double> sign{-1,1};
  37. int trunced = 0;
  38. std::uniform_real_distribution<long double> number{
  39. std::numeric_limits<long double>::min(),
  40. (long double)std::numeric_limits<std::intmax_t>::max(),
  41. };
  42. for(int i = 0; i < 10'000'000; ++i)
  43. {
  44. long double a = std::copysign(number(random), sign(random));
  45. assert( trunc_up_to_intmax(a) == std::trunc(a) );
  46. if(trunc_up_to_intmax(a) != a)
  47. ++trunced;
  48. };
  49. assert(trunced > 0);
  50. trunced = 0;
  51. std::uniform_real_distribution<long double> bigger_number{
  52. (long double)std::numeric_limits<std::intmax_t>::max(),
  53. std::numeric_limits<long double>::max(),
  54. };
  55. for(int i = 0; i < 10'000'000; ++i)
  56. {
  57. long double a = std::copysign(bigger_number(random), sign(random));
  58. assert( trunc_up_to_intmax(a) == std::trunc(a) );
  59. if(trunc_up_to_intmax(a) != a)
  60. ++trunced;
  61. };
  62. assert(0 == trunced);
  63. }
  64. void BabelonianSquareRoot()
  65. {
  66. auto seed = std::random_device{}();
  67. std::cout << "Babelonian square root test seed: " << std::hex << std::showbase << seed << std::dec;
  68. random::engine::tiny<unsigned long long> random{seed};
  69. std::uniform_real_distribution<double> number{};
  70. double good = 0;
  71. double bad = 0;
  72. int awful = 0;
  73. auto then = high_resolution_clock::now();
  74. for(int i = 0; i < 10'000'000; ++i)
  75. {
  76. double n = number(random);
  77. if((std::abs(babelonian_root2_f(n) - std::sqrt(n)) <
  78. (std::sqrt(n) * std::numeric_limits<double>::epsilon())))
  79. ++good;
  80. else
  81. ++bad;
  82. if(!almost_equal(babelonian_root2_f(n) , std::sqrt(n)))
  83. ++awful;
  84. }
  85. std::cout << " time: " << (high_resolution_clock::now() - then).count() << std::endl;
  86. assert( (good / (good+bad)) > 0.99 );
  87. assert( awful == 0 );
  88. // edge cases
  89. assert( std::isnan(babelonian_root2_f(-1.f)) );
  90. assert( std::isnan(babelonian_root2_f(0.f/0.f)) );
  91. float x = 0;
  92. assert( babelonian_root2_f(x) == x );
  93. x = std::numeric_limits<float>::min()/2;
  94. assert( babelonian_root2_f(x) == x );
  95. x = std::numeric_limits<float>::max()*2;
  96. assert( babelonian_root2_f(x) == x );
  97. }
  98. void Babelonian4thRoot()
  99. {
  100. auto seed = std::random_device{}();
  101. std::cout << "Babelonian 4th root test seed: " << std::hex << std::showbase << seed << std::dec;
  102. random::engine::tiny<unsigned long long> random{seed};
  103. std::uniform_real_distribution<double> number{};
  104. double good = 0;
  105. double bad = 0;
  106. int awful = 0;
  107. auto then = high_resolution_clock::now();
  108. for(int i = 0; i < 10'000'000; ++i)
  109. {
  110. double n = number(random);
  111. if((std::abs(babelonian_root2_f(babelonian_root2_f(n)) - std::pow(n,1.0/4)) <
  112. (std::pow(n,1.0/4) * std::numeric_limits<double>::epsilon())))
  113. ++good;
  114. else
  115. ++bad;
  116. if(!almost_equal(babelonian_root2_f(babelonian_root2_f(n)) , std::pow(n,1.0/4)))
  117. ++awful;
  118. }
  119. std::cout << " time: " << (high_resolution_clock::now() - then).count() << std::endl;
  120. assert( (good / (good+bad)) > 0.99 );
  121. assert( awful == 0 );
  122. // edge cases
  123. assert( std::isnan(babelonian_root2_f(-1.f)) );
  124. assert( std::isnan(babelonian_root2_f(0.f/0.f)) );
  125. float x = 0;
  126. assert( babelonian_root2_f(x) == x );
  127. x = std::numeric_limits<float>::min()/2;
  128. assert( babelonian_root2_f(x) == x );
  129. x = std::numeric_limits<float>::max()*2;
  130. assert( babelonian_root2_f(x) == x );
  131. }
  132. template <unsigned Degree>
  133. void NewtonianRoot()
  134. {
  135. double power = 1.0/Degree;
  136. auto seed = std::random_device{}();
  137. std::cout << "Newtonian root of degree " << std::dec << Degree << " test seed: " << std::hex << std::showbase << seed << std::dec;
  138. random::engine::tiny<unsigned long long> random{seed};
  139. std::uniform_real_distribution<double> number{};
  140. // with all the powers involved newton sometimes won't even terminate with default definition check, so instead have to use a convergence check
  141. // TODO: figure out what's wrong with the default, or make this default
  142. auto cnvrg = [prev = 0.0](auto guess) mutable
  143. {
  144. auto ret = almost_equal(prev, guess);
  145. prev = guess;
  146. return ret;
  147. };
  148. double good = 0;
  149. double bad = 0;
  150. int awful = 0;
  151. constexpr int count = 10'000'000;
  152. auto then = high_resolution_clock::now();
  153. for(int i = 0; i < count; ++i)
  154. {
  155. double n = number(random);
  156. if((std::abs(newtonian_root_f<Degree>(n,cnvrg) - std::pow(n,power)) <
  157. (std::pow(n,power) * std::numeric_limits<double>::epsilon())))
  158. ++good;
  159. else
  160. ++bad;
  161. if(!almost_equal(newtonian_root_f<Degree>(n,cnvrg) , std::pow(n,power)))
  162. ++awful;
  163. }
  164. std::cout << " time: " << (high_resolution_clock::now() - then).count() << std::endl;
  165. assert( (good / (good+bad)) > 0.99 );
  166. assert( awful == 0 );
  167. // edge cases
  168. assert( (Degree % 2 == 1) || std::isnan(newtonian_root_f<Degree>(-1.f)) );
  169. assert( std::isnan(newtonian_root_f<Degree>(0.f/0.f)) );
  170. float x = 0;
  171. assert( newtonian_root_f<Degree>(x) == x );
  172. x = std::numeric_limits<float>::min()/2;
  173. assert( newtonian_root_f<Degree>(x) == x );
  174. x = std::numeric_limits<float>::max()*2;
  175. assert( newtonian_root_f<Degree>(x) == x );
  176. }
  177. constexpr bool Constexpr()
  178. {
  179. void(root2(2.0));
  180. return true;
  181. }
  182. int main()
  183. {
  184. FloatEquality();
  185. FloatTruncation();
  186. BabelonianSquareRoot();
  187. Babelonian4thRoot();
  188. NewtonianRoot<1>();
  189. NewtonianRoot<2>();
  190. NewtonianRoot<3>();
  191. NewtonianRoot<4>();
  192. NewtonianRoot<5>();
  193. static_assert(Constexpr(),"");
  194. return 0;
  195. }