numeric_util_test_suite.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. // Copyright (c) 2014-2021 Thomas Fussell
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy
  4. // of this software and associated documentation files (the "Software"), to deal
  5. // in the Software without restriction, including without limitation the rights
  6. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. // copies of the Software, and to permit persons to whom the Software is
  8. // furnished to do so, subject to the following conditions:
  9. //
  10. // The above copyright notice and this permission notice shall be included in
  11. // all copies or substantial portions of the Software.
  12. //
  13. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. // THE SOFTWARE
  20. //
  21. // @license: http://www.opensource.org/licenses/mit-license.php
  22. // @author: see AUTHORS file
  23. #include <xlnt/utils/numeric.hpp>
  24. #include <helpers/test_suite.hpp>
  25. class numeric_test_suite : public test_suite
  26. {
  27. public:
  28. numeric_test_suite()
  29. {
  30. register_test(test_serialise_number);
  31. register_test(test_float_equals_zero);
  32. register_test(test_float_equals_large);
  33. register_test(test_float_equals_fairness);
  34. register_test(test_min);
  35. register_test(test_max);
  36. register_test(test_abs);
  37. }
  38. void test_serialise_number()
  39. {
  40. xlnt::detail::number_serialiser serialiser;
  41. // excel serialises numbers as floating point values with <= 15 digits of precision
  42. xlnt_assert(serialiser.serialise(1) == "1");
  43. // trailing zeroes are ignored
  44. xlnt_assert(serialiser.serialise(1.0) == "1");
  45. xlnt_assert(serialiser.serialise(1.0f) == "1");
  46. // one to 1 relation
  47. xlnt_assert(serialiser.serialise(1.23456) == "1.23456");
  48. xlnt_assert(serialiser.serialise(1.23456789012345) == "1.23456789012345");
  49. xlnt_assert(serialiser.serialise(123456.789012345) == "123456.789012345");
  50. xlnt_assert(serialiser.serialise(1.23456789012345e+67) == "1.23456789012345e+67");
  51. xlnt_assert(serialiser.serialise(1.23456789012345e-67) == "1.23456789012345e-67");
  52. }
  53. void test_float_equals_zero()
  54. {
  55. // comparing relatively small numbers (2.3e-6) with 0 will be true by default
  56. const float comp_val = 2.3e-6f; // about the largest difference allowed by default
  57. xlnt_assert(0.f != comp_val); // fail because not exactly equal
  58. xlnt_assert(xlnt::detail::float_equals(0.0, comp_val));
  59. xlnt_assert(xlnt::detail::float_equals(0.0, -comp_val));
  60. // fail because diff is out of bounds for fuzzy equality
  61. xlnt_assert(!xlnt::detail::float_equals(0.0, comp_val + 0.1e-6));
  62. xlnt_assert(!xlnt::detail::float_equals(0.0, -(comp_val + 0.1e-6)));
  63. // if the bounds of comparison are too loose, there are two tweakable knobs to tighten the comparison up
  64. //==========================================================
  65. // #1: reduce the epsilon_scale (default is 20)
  66. // This can bring the range down to FLT_EPSILON (scale factor of 1)
  67. xlnt_assert(!xlnt::detail::float_equals(0.0, comp_val, 10));
  68. const float closer_comp_val = 1.1e-6f;
  69. xlnt_assert(xlnt::detail::float_equals(0.0, closer_comp_val, 10));
  70. xlnt_assert(!xlnt::detail::float_equals(0.0, closer_comp_val + 0.1e-6, 10));
  71. xlnt_assert(xlnt::detail::float_equals(0.0, -closer_comp_val, 10));
  72. xlnt_assert(!xlnt::detail::float_equals(0.0, -(closer_comp_val + 0.1e-6), 10));
  73. //==========================================================
  74. // #2: specify the epsilon source as a higher precision type (e.g. double)
  75. // This makes the epsilon range quite significantly less
  76. xlnt_assert(!xlnt::detail::float_equals<double>(0.0, comp_val));
  77. xlnt_assert(!xlnt::detail::float_equals<double>(0.0, closer_comp_val));
  78. const float tiny_comp_val = 4.4e-15f;
  79. xlnt_assert(xlnt::detail::float_equals<double>(0.0, tiny_comp_val));
  80. xlnt_assert(!xlnt::detail::float_equals<double>(0.0, tiny_comp_val + 0.1e-15));
  81. xlnt_assert(xlnt::detail::float_equals<double>(0.0, -tiny_comp_val));
  82. xlnt_assert(!xlnt::detail::float_equals<double>(0.0, -(tiny_comp_val + 0.1e-15)));
  83. //==========================================================
  84. // #3: combine #1 & #2
  85. // for the tightest default precision, double with a scale of 1
  86. xlnt_assert(!xlnt::detail::float_equals<double>(0.0, comp_val, 1));
  87. xlnt_assert(!xlnt::detail::float_equals<double>(0.0, closer_comp_val, 1));
  88. xlnt_assert(!xlnt::detail::float_equals<double>(0.0, tiny_comp_val, 1));
  89. const float really_tiny_comp_val = 2.2e-16f; // the limit is +/- std::numeric_limits<double>::epsilon()
  90. xlnt_assert(xlnt::detail::float_equals<double>(0.0, really_tiny_comp_val, 1));
  91. xlnt_assert(!xlnt::detail::float_equals<double>(0.0, really_tiny_comp_val + 0.1e-16, 1));
  92. xlnt_assert(xlnt::detail::float_equals<double>(0.0, -really_tiny_comp_val, 1));
  93. xlnt_assert(!xlnt::detail::float_equals<double>(0.0, -(really_tiny_comp_val + 0.1e-16), 1));
  94. //==========================================================
  95. // in the world of floats, 2.2e-16 is still significantly different to 0.f (smallest representable float is around 1e-38)
  96. // if comparisons are known to involve extremely small numbers (such that +/- 2.2e-16 is too large a band),
  97. // a type that specialises std::numeric_limits::epsilon may be passed as the first template parameter
  98. // the type itself doesn't actually need to have any behaviour as it is only used as the source for epsilon
  99. // struct super_precise{};
  100. // namespace std {
  101. // template<> numeric_limits<super_precise> {
  102. // double epsilon() {
  103. // return 1e-30;
  104. // }
  105. // }
  106. // }
  107. // float_equals<double>(0.0, 2e-30, 1); // returns true
  108. // float_equals<super_precise>(0.0, 2e-30, 1); // returns false
  109. }
  110. void test_float_equals_large()
  111. {
  112. const float compare_to = 20e6;
  113. // fp math with arguments of different magnitudes is wierd
  114. xlnt_assert(compare_to == compare_to + 1); // x == x + 1 ...
  115. xlnt_assert(compare_to != compare_to + 10); // x != x + 10
  116. xlnt_assert(compare_to != compare_to - 10); // x != x - 10
  117. // if the same epsilon was used for comparison of large values as the values around one
  118. // we'd have all the issues around zero again
  119. xlnt_assert(xlnt::detail::float_equals(compare_to, compare_to + 49));
  120. xlnt_assert(!xlnt::detail::float_equals(compare_to, compare_to + 50));
  121. xlnt_assert(xlnt::detail::float_equals(compare_to, compare_to - 49));
  122. xlnt_assert(!xlnt::detail::float_equals(compare_to, compare_to - 50));
  123. // float_equals also scales epsilon up to match the magnitude of its arguments
  124. // all the same options are available for increasing/decreasing the precision of the comparison
  125. // however the the epsilon source should always be of equal or lesser precision than the arguments when away from zero
  126. }
  127. void test_float_equals_nan()
  128. {
  129. const float nan = std::nanf("");
  130. // nans always compare false
  131. xlnt_assert(!xlnt::detail::float_equals(nan, 0.f));
  132. xlnt_assert(!xlnt::detail::float_equals(nan, nan));
  133. xlnt_assert(!xlnt::detail::float_equals(nan, 1000.f));
  134. }
  135. void test_float_equals_fairness()
  136. {
  137. // tests for parameter ordering dependency
  138. // (lhs ~= rhs) == (rhs ~= lhs)
  139. const double test_val = 1.0;
  140. const double test_diff_pass = 1.192092e-07; // should all pass with this
  141. const double test_diff = 1.192093e-07; // difference enough to provide different results if the comparison is not "fair"
  142. const double test_diff_fails = 1.192094e-07; // should all fail with this
  143. // test_diff_pass
  144. xlnt_assert(xlnt::detail::float_equals<float>((test_val + test_diff_pass), test_val, 1));
  145. xlnt_assert(xlnt::detail::float_equals<float>(test_val, (test_val + test_diff_pass), 1));
  146. xlnt_assert(xlnt::detail::float_equals<float>(-(test_val + test_diff_pass), -test_val, 1));
  147. xlnt_assert(xlnt::detail::float_equals<float>(-test_val, -(test_val + test_diff_pass), 1));
  148. // test_diff
  149. xlnt_assert(xlnt::detail::float_equals<float>((test_val + test_diff), test_val, 1));
  150. xlnt_assert(xlnt::detail::float_equals<float>(test_val, (test_val + test_diff), 1));
  151. xlnt_assert(xlnt::detail::float_equals<float>(-(test_val + test_diff), -test_val, 1));
  152. xlnt_assert(xlnt::detail::float_equals<float>(-test_val, -(test_val + test_diff), 1));
  153. // test_diff_fails
  154. xlnt_assert(!xlnt::detail::float_equals<float>((test_val + test_diff_fails), test_val, 1));
  155. xlnt_assert(!xlnt::detail::float_equals<float>(test_val, (test_val + test_diff_fails), 1));
  156. xlnt_assert(!xlnt::detail::float_equals<float>(-(test_val + test_diff_fails), -test_val, 1));
  157. xlnt_assert(!xlnt::detail::float_equals<float>(-test_val, -(test_val + test_diff_fails), 1));
  158. }
  159. void test_min()
  160. {
  161. // simple
  162. xlnt_assert(xlnt::detail::min(0, 1) == 0);
  163. xlnt_assert(xlnt::detail::min(1, 0) == 0);
  164. xlnt_assert(xlnt::detail::min(0.0, 1) == 0.0); // comparisons between different types just work
  165. xlnt_assert(xlnt::detail::min(1, 0.0) == 0.0);
  166. // negative numbers
  167. xlnt_assert(xlnt::detail::min(0, -1) == -1.0);
  168. xlnt_assert(xlnt::detail::min(-1, 0) == -1.0);
  169. xlnt_assert(xlnt::detail::min(0.0, -1) == -1.0);
  170. xlnt_assert(xlnt::detail::min(-1, 0.0) == -1.0);
  171. // no zeroes
  172. xlnt_assert(xlnt::detail::min(10, -10) == -10.0);
  173. xlnt_assert(xlnt::detail::min(-10, 10) == -10.0);
  174. xlnt_assert(xlnt::detail::min(10.0, -10) == -10.0);
  175. xlnt_assert(xlnt::detail::min(-10, 10.0) == -10.0);
  176. static_assert(xlnt::detail::min(-10, 10.0) == -10.0, "constexpr");
  177. }
  178. void test_max()
  179. {
  180. // simple
  181. xlnt_assert(xlnt::detail::max(0, 1) == 1);
  182. xlnt_assert(xlnt::detail::max(1, 0) == 1);
  183. xlnt_assert(xlnt::detail::max(0.0, 1) == 1.0); // comparisons between different types just work
  184. xlnt_assert(xlnt::detail::max(1, 0.0) == 1.0);
  185. // negative numbers
  186. xlnt_assert(xlnt::detail::max(0, -1) == 0.0);
  187. xlnt_assert(xlnt::detail::max(-1, 0) == 0.0);
  188. xlnt_assert(xlnt::detail::max(0.0, -1) == 0.0);
  189. xlnt_assert(xlnt::detail::max(-1, 0.0) == 0.0);
  190. // no zeroes
  191. xlnt_assert(xlnt::detail::max(10, -10) == 10.0);
  192. xlnt_assert(xlnt::detail::max(-10, 10) == 10.0);
  193. xlnt_assert(xlnt::detail::max(10.0, -10) == 10.0);
  194. xlnt_assert(xlnt::detail::max(-10, 10.0) == 10.0);
  195. static_assert(xlnt::detail::max(-10, 10.0) == 10.0, "constexpr");
  196. }
  197. void test_abs()
  198. {
  199. xlnt_assert(xlnt::detail::abs(0) == 0);
  200. xlnt_assert(xlnt::detail::abs(1) == 1);
  201. xlnt_assert(xlnt::detail::abs(-1) == 1);
  202. xlnt_assert(xlnt::detail::abs(0.0) == 0.0);
  203. xlnt_assert(xlnt::detail::abs(1.5) == 1.5);
  204. xlnt_assert(xlnt::detail::abs(-1.5) == 1.5);
  205. static_assert(xlnt::detail::abs(-1.23) == 1.23, "constexpr");
  206. }
  207. };
  208. static numeric_test_suite x;