numeric_traits_test.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. // (C) Copyright David Abrahams 2001.
  2. // Distributed under the Boost Software License, Version 1.0. (See
  3. // accompanying file LICENSE_1_0.txt or copy at
  4. // http://www.boost.org/LICENSE_1_0.txt)
  5. // See http://www.boost.org for most recent version including documentation.
  6. // Revision History
  7. // 1 Apr 2001 Fixes for ICL; use BOOST_STATIC_CONSTANT
  8. // 11 Feb 2001 Fixes for Borland (David Abrahams)
  9. // 23 Jan 2001 Added test for wchar_t (David Abrahams)
  10. // 23 Jan 2001 Now statically selecting a test for signed numbers to avoid
  11. // warnings with fancy compilers. Added commentary and
  12. // additional dumping of traits data for tested types (David
  13. // Abrahams).
  14. // 21 Jan 2001 Initial version (David Abrahams)
  15. #include <boost/detail/numeric_traits.hpp>
  16. #include <cassert>
  17. #include <boost/type_traits/conditional.hpp>
  18. #include <boost/type_traits/is_signed.hpp>
  19. #include <boost/type_traits/is_same.hpp>
  20. #include <boost/static_assert.hpp>
  21. #include <boost/cstdint.hpp>
  22. #include <climits>
  23. #include <typeinfo>
  24. #include <iostream>
  25. #include <sstream>
  26. #include <string>
  27. #ifndef BOOST_NO_LIMITS
  28. #include <limits>
  29. #endif
  30. // =================================================================================
  31. // template class complement_traits<Number> --
  32. //
  33. // statically computes the max and min for 1s and 2s-complement binary
  34. // numbers. This helps on platforms without <limits> support. It also shows
  35. // an example of a recursive template that works with MSVC!
  36. //
  37. template <unsigned size> struct complement; // forward
  38. // The template complement, below, does all the real work, using "poor man's
  39. // partial specialization". We need complement_traits_aux<> so that MSVC doesn't
  40. // complain about undefined min/max as we're trying to recursively define them.
  41. template <class Number, unsigned size>
  42. struct complement_traits_aux
  43. {
  44. BOOST_STATIC_CONSTANT(Number, max = complement<size>::template traits<Number>::max);
  45. BOOST_STATIC_CONSTANT(Number, min = complement<size>::template traits<Number>::min);
  46. };
  47. template <unsigned size>
  48. struct complement
  49. {
  50. template <class Number>
  51. struct traits
  52. {
  53. private:
  54. // indirection through complement_traits_aux necessary to keep MSVC happy
  55. typedef complement_traits_aux<Number, size - 1> prev;
  56. public:
  57. #if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 0 && __GNUC_PATCHLEVEL__ == 2
  58. // GCC 4.0.2 ICEs on these C-style casts
  59. BOOST_STATIC_CONSTANT(Number, max =
  60. Number((prev::max) << CHAR_BIT)
  61. + Number(UCHAR_MAX));
  62. BOOST_STATIC_CONSTANT(Number, min = Number((prev::min) << CHAR_BIT));
  63. #else
  64. // Avoid left shifting negative integers, use multiplication instead
  65. BOOST_STATIC_CONSTANT(Number, shift = 1u << CHAR_BIT);
  66. BOOST_STATIC_CONSTANT(Number, max =
  67. Number(Number(prev::max) * shift)
  68. + Number(UCHAR_MAX));
  69. BOOST_STATIC_CONSTANT(Number, min = Number(Number(prev::min) * shift));
  70. #endif
  71. };
  72. };
  73. // Template class complement_base<> -- defines values for min and max for
  74. // complement<1>, at the deepest level of recursion. Uses "poor man's partial
  75. // specialization" again.
  76. template <bool is_signed> struct complement_base;
  77. template <> struct complement_base<false>
  78. {
  79. template <class Number>
  80. struct values
  81. {
  82. BOOST_STATIC_CONSTANT(Number, min = 0);
  83. BOOST_STATIC_CONSTANT(Number, max = UCHAR_MAX);
  84. };
  85. };
  86. template <> struct complement_base<true>
  87. {
  88. template <class Number>
  89. struct values
  90. {
  91. BOOST_STATIC_CONSTANT(Number, min = SCHAR_MIN);
  92. BOOST_STATIC_CONSTANT(Number, max = SCHAR_MAX);
  93. };
  94. };
  95. // Base specialization of complement, puts an end to the recursion.
  96. template <>
  97. struct complement<1>
  98. {
  99. template <class Number>
  100. struct traits
  101. {
  102. BOOST_STATIC_CONSTANT(bool, is_signed = boost::is_signed<Number>::value);
  103. BOOST_STATIC_CONSTANT(Number, min =
  104. complement_base<is_signed>::template values<Number>::min);
  105. BOOST_STATIC_CONSTANT(Number, max =
  106. complement_base<is_signed>::template values<Number>::max);
  107. };
  108. };
  109. // Now here's the "pretty" template you're intended to actually use.
  110. // complement_traits<Number>::min, complement_traits<Number>::max are the
  111. // minimum and maximum values of Number if Number is a built-in integer type.
  112. template <class Number>
  113. struct complement_traits
  114. {
  115. BOOST_STATIC_CONSTANT(Number, max = (complement_traits_aux<Number, sizeof(Number)>::max));
  116. BOOST_STATIC_CONSTANT(Number, min = (complement_traits_aux<Number, sizeof(Number)>::min));
  117. };
  118. // =================================================================================
  119. // Support for streaming various numeric types in exactly the format I want. I
  120. // needed this in addition to all the assertions so that I could see exactly
  121. // what was going on.
  122. //
  123. // Numbers go through a 2-stage conversion process (by default, though, no real
  124. // conversion).
  125. //
  126. template <class T> struct stream_as {
  127. typedef T t1;
  128. typedef T t2;
  129. };
  130. // char types first get converted to unsigned char, then to unsigned.
  131. template <> struct stream_as<char> {
  132. typedef unsigned char t1;
  133. typedef unsigned t2;
  134. };
  135. template <> struct stream_as<unsigned char> {
  136. typedef unsigned char t1; typedef unsigned t2;
  137. };
  138. template <> struct stream_as<signed char> {
  139. typedef unsigned char t1; typedef unsigned t2;
  140. };
  141. #if defined(BOOST_MSVC_STD_ITERATOR) // No intmax streaming built-in
  142. // With this library implementation, __int64 and __uint64 get streamed as strings
  143. template <> struct stream_as<boost::uintmax_t> {
  144. typedef std::string t1;
  145. typedef std::string t2;
  146. };
  147. template <> struct stream_as<boost::intmax_t> {
  148. typedef std::string t1;
  149. typedef std::string t2;
  150. };
  151. #endif
  152. // Standard promotion process for streaming
  153. template <class T> struct promote
  154. {
  155. static typename stream_as<T>::t1 from(T x) {
  156. typedef typename stream_as<T>::t1 t1;
  157. return t1(x);
  158. }
  159. };
  160. #if defined(BOOST_MSVC_STD_ITERATOR) // No intmax streaming built-in
  161. // On this platform, stream them as long/unsigned long if they fit.
  162. // Otherwise, write a string.
  163. template <> struct promote<boost::uintmax_t> {
  164. std::string static from(const boost::uintmax_t x) {
  165. if (x > ULONG_MAX)
  166. return std::string("large unsigned value");
  167. else {
  168. std::ostringstream strm;
  169. strm << (unsigned long)x;
  170. return strm.str();
  171. }
  172. }
  173. };
  174. template <> struct promote<boost::intmax_t> {
  175. std::string static from(const boost::intmax_t x) {
  176. if (x > boost::intmax_t(ULONG_MAX))
  177. return std::string("large positive signed value");
  178. else if (x >= 0) {
  179. std::ostringstream strm;
  180. strm << (unsigned long)x;
  181. return strm.str();
  182. }
  183. if (x < boost::intmax_t(LONG_MIN))
  184. return std::string("large negative signed value");
  185. else {
  186. std::ostringstream strm;
  187. strm << (long)x;
  188. return strm.str();
  189. }
  190. }
  191. };
  192. #endif
  193. // This is the function which converts types to the form I want to stream them in.
  194. template <class T>
  195. typename stream_as<T>::t2 stream_number(T x)
  196. {
  197. return promote<T>::from(x);
  198. }
  199. // =================================================================================
  200. //
  201. // Tests for built-in signed and unsigned types
  202. //
  203. // Tag types for selecting tests
  204. struct unsigned_tag {};
  205. struct signed_tag {};
  206. // Tests for unsigned numbers. The extra default Number parameter works around
  207. // an MSVC bug.
  208. template <class Number>
  209. void test_aux(unsigned_tag, Number*)
  210. {
  211. typedef typename boost::detail::numeric_traits<Number>::difference_type difference_type;
  212. BOOST_STATIC_ASSERT(!boost::is_signed<Number>::value);
  213. BOOST_STATIC_ASSERT(
  214. (sizeof(Number) < sizeof(boost::intmax_t))
  215. | (boost::is_same<difference_type, boost::intmax_t>::value));
  216. #if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 0 && __GNUC_PATCHLEVEL__ == 2
  217. // GCC 4.0.2 ICEs on this C-style cases
  218. BOOST_STATIC_ASSERT((complement_traits<Number>::max) > Number(0));
  219. BOOST_STATIC_ASSERT((complement_traits<Number>::min) == Number(0));
  220. #else
  221. // Force casting to Number here to work around the fact that it's an enum on MSVC
  222. BOOST_STATIC_ASSERT(Number(complement_traits<Number>::max) > Number(0));
  223. BOOST_STATIC_ASSERT(Number(complement_traits<Number>::min) == Number(0));
  224. #endif
  225. const Number max = complement_traits<Number>::max;
  226. const Number min = complement_traits<Number>::min;
  227. const Number test_max = (sizeof(Number) < sizeof(boost::intmax_t))
  228. ? max
  229. : max / 2 - 1;
  230. std::cout << std::hex << "(unsigned) min = " << stream_number(min) << ", max = "
  231. << stream_number(max) << "..." << std::flush;
  232. std::cout << "difference_type = " << typeid(difference_type).name() << "..."
  233. << std::flush;
  234. difference_type d1 = boost::detail::numeric_distance(Number(0), test_max);
  235. difference_type d2 = boost::detail::numeric_distance(test_max, Number(0));
  236. std::cout << "0->" << stream_number(test_max) << "==" << std::dec << stream_number(d1) << "; "
  237. << std::hex << stream_number(test_max) << "->0==" << std::dec << stream_number(d2) << "..." << std::flush;
  238. assert(d1 == difference_type(test_max));
  239. assert(d2 == -difference_type(test_max));
  240. }
  241. // Tests for signed numbers. The extra default Number parameter works around an
  242. // MSVC bug.
  243. struct out_of_range_tag {};
  244. struct in_range_tag {};
  245. // This test morsel gets executed for numbers whose difference will always be
  246. // representable in intmax_t
  247. template <class Number>
  248. void signed_test(in_range_tag, Number*)
  249. {
  250. BOOST_STATIC_ASSERT(boost::is_signed<Number>::value);
  251. typedef typename boost::detail::numeric_traits<Number>::difference_type difference_type;
  252. const Number max = complement_traits<Number>::max;
  253. const Number min = complement_traits<Number>::min;
  254. difference_type d1 = boost::detail::numeric_distance(min, max);
  255. difference_type d2 = boost::detail::numeric_distance(max, min);
  256. std::cout << stream_number(min) << "->" << stream_number(max) << "==";
  257. std::cout << std::dec << stream_number(d1) << "; ";
  258. std::cout << std::hex << stream_number(max) << "->" << stream_number(min)
  259. << "==" << std::dec << stream_number(d2) << "..." << std::flush;
  260. assert(d1 == difference_type(max) - difference_type(min));
  261. assert(d2 == difference_type(min) - difference_type(max));
  262. }
  263. // This test morsel gets executed for numbers whose difference may exceed the
  264. // capacity of intmax_t.
  265. template <class Number>
  266. void signed_test(out_of_range_tag, Number*)
  267. {
  268. BOOST_STATIC_ASSERT(boost::is_signed<Number>::value);
  269. typedef typename boost::detail::numeric_traits<Number>::difference_type difference_type;
  270. const Number max = complement_traits<Number>::max;
  271. const Number min = complement_traits<Number>::min;
  272. difference_type min_distance = complement_traits<difference_type>::min;
  273. difference_type max_distance = complement_traits<difference_type>::max;
  274. const Number n1 = Number(min + max_distance);
  275. const Number n2 = Number(max + min_distance);
  276. difference_type d1 = boost::detail::numeric_distance(min, n1);
  277. difference_type d2 = boost::detail::numeric_distance(max, n2);
  278. std::cout << stream_number(min) << "->" << stream_number(n1) << "==";
  279. std::cout << std::dec << stream_number(d1) << "; ";
  280. std::cout << std::hex << stream_number(max) << "->" << stream_number(n2)
  281. << "==" << std::dec << stream_number(d2) << "..." << std::flush;
  282. assert(d1 == max_distance);
  283. assert(d2 == min_distance);
  284. }
  285. template <class Number>
  286. void test_aux(signed_tag, Number*)
  287. {
  288. typedef typename boost::detail::numeric_traits<Number>::difference_type difference_type;
  289. BOOST_STATIC_ASSERT(boost::is_signed<Number>::value);
  290. BOOST_STATIC_ASSERT(
  291. (sizeof(Number) < sizeof(boost::intmax_t))
  292. | (boost::is_same<difference_type, Number>::value));
  293. #if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 0 && __GNUC_PATCHLEVEL__ == 2
  294. // GCC 4.0.2 ICEs on this cast
  295. BOOST_STATIC_ASSERT((complement_traits<Number>::max) > Number(0));
  296. BOOST_STATIC_ASSERT((complement_traits<Number>::min) < Number(0));
  297. #else
  298. // Force casting to Number here to work around the fact that it's an enum on MSVC
  299. BOOST_STATIC_ASSERT(Number(complement_traits<Number>::max) > Number(0));
  300. BOOST_STATIC_ASSERT(Number(complement_traits<Number>::min) < Number(0));
  301. #endif
  302. const Number max = complement_traits<Number>::max;
  303. const Number min = complement_traits<Number>::min;
  304. std::cout << std::hex << "min = " << stream_number(min) << ", max = "
  305. << stream_number(max) << "..." << std::flush;
  306. std::cout << "difference_type = " << typeid(difference_type).name() << "..."
  307. << std::flush;
  308. typedef typename boost::conditional<
  309. (sizeof(Number) < sizeof(boost::intmax_t)),
  310. in_range_tag,
  311. out_of_range_tag
  312. >::type range_tag;
  313. signed_test<Number>(range_tag(), 0);
  314. }
  315. // Test for all numbers. The extra default Number parameter works around an MSVC
  316. // bug.
  317. template <class Number>
  318. void test(Number* = 0)
  319. {
  320. std::cout << "testing " << typeid(Number).name() << ":\n"
  321. #ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
  322. << "is_signed: " << (std::numeric_limits<Number>::is_signed ? "true\n" : "false\n")
  323. << "is_bounded: " << (std::numeric_limits<Number>::is_bounded ? "true\n" : "false\n")
  324. << "digits: " << std::numeric_limits<Number>::digits << "\n"
  325. #endif
  326. << "..." << std::flush;
  327. // factoring out difference_type for the assert below confused Borland :(
  328. typedef boost::is_signed<
  329. #if !defined(BOOST_MSVC) || BOOST_MSVC > 1300
  330. typename
  331. #endif
  332. boost::detail::numeric_traits<Number>::difference_type
  333. > is_signed;
  334. BOOST_STATIC_ASSERT(is_signed::value);
  335. typedef typename boost::conditional<
  336. boost::is_signed<Number>::value,
  337. signed_tag,
  338. unsigned_tag
  339. >::type signedness;
  340. test_aux<Number>(signedness(), 0);
  341. std::cout << "passed" << std::endl;
  342. }
  343. int main()
  344. {
  345. test<char>();
  346. test<unsigned char>();
  347. test<signed char>();
  348. test<wchar_t>();
  349. test<short>();
  350. test<unsigned short>();
  351. test<int>();
  352. test<unsigned int>();
  353. test<long>();
  354. test<unsigned long>();
  355. #if defined(BOOST_HAS_LONG_LONG) && !defined(BOOST_NO_INTEGRAL_INT64_T)
  356. test< ::boost::long_long_type>();
  357. test< ::boost::ulong_long_type>();
  358. #elif defined(BOOST_MSVC)
  359. // The problem of not having compile-time static class constants other than
  360. // enums prevents this from working, since values get truncated.
  361. // test<boost::uintmax_t>();
  362. // test<boost::intmax_t>();
  363. #endif
  364. return 0;
  365. }