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)
6 // See http://www.boost.org for most recent version including documentation.
9 // 1 Apr 2001 Fixes for ICL; use BOOST_STATIC_CONSTANT
10 // 11 Feb 2001 Fixes for Borland (David Abrahams)
11 // 23 Jan 2001 Added test for wchar_t (David Abrahams)
12 // 23 Jan 2001 Now statically selecting a test for signed numbers to avoid
13 // warnings with fancy compilers. Added commentary and
14 // additional dumping of traits data for tested types (David
16 // 21 Jan 2001 Initial version (David Abrahams)
18 #include <boost/detail/numeric_traits.hpp>
20 #include <boost/type_traits/conditional.hpp>
21 #include <boost/type_traits/is_signed.hpp>
22 #include <boost/type_traits/is_same.hpp>
23 #include <boost/static_assert.hpp>
24 #include <boost/cstdint.hpp>
30 #ifndef BOOST_NO_LIMITS
34 // =================================================================================
35 // template class complement_traits<Number> --
37 // statically computes the max and min for 1s and 2s-complement binary
38 // numbers. This helps on platforms without <limits> support. It also shows
39 // an example of a recursive template that works with MSVC!
42 template <unsigned size
> struct complement
; // forward
44 // The template complement, below, does all the real work, using "poor man's
45 // partial specialization". We need complement_traits_aux<> so that MSVC doesn't
46 // complain about undefined min/max as we're trying to recursively define them.
47 template <class Number
, unsigned size
>
48 struct complement_traits_aux
50 BOOST_STATIC_CONSTANT(Number
, max
= complement
<size
>::template traits
<Number
>::max
);
51 BOOST_STATIC_CONSTANT(Number
, min
= complement
<size
>::template traits
<Number
>::min
);
54 template <unsigned size
>
57 template <class Number
>
61 // indirection through complement_traits_aux necessary to keep MSVC happy
62 typedef complement_traits_aux
<Number
, size
- 1> prev
;
64 #if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 0 && __GNUC_PATCHLEVEL__ == 2
65 // GCC 4.0.2 ICEs on these C-style casts
66 BOOST_STATIC_CONSTANT(Number
, max
=
67 Number((prev::max
) << CHAR_BIT
)
69 BOOST_STATIC_CONSTANT(Number
, min
= Number((prev::min
) << CHAR_BIT
));
71 // Avoid left shifting negative integers, use multiplication instead
72 BOOST_STATIC_CONSTANT(Number
, shift
= 1u << CHAR_BIT
);
73 BOOST_STATIC_CONSTANT(Number
, max
=
74 Number(Number(prev::max
) * shift
)
76 BOOST_STATIC_CONSTANT(Number
, min
= Number(Number(prev::min
) * shift
));
81 // Template class complement_base<> -- defines values for min and max for
82 // complement<1>, at the deepest level of recursion. Uses "poor man's partial
83 // specialization" again.
84 template <bool is_signed
> struct complement_base
;
86 template <> struct complement_base
<false>
88 template <class Number
>
91 BOOST_STATIC_CONSTANT(Number
, min
= 0);
92 BOOST_STATIC_CONSTANT(Number
, max
= UCHAR_MAX
);
96 template <> struct complement_base
<true>
98 template <class Number
>
101 BOOST_STATIC_CONSTANT(Number
, min
= SCHAR_MIN
);
102 BOOST_STATIC_CONSTANT(Number
, max
= SCHAR_MAX
);
106 // Base specialization of complement, puts an end to the recursion.
110 template <class Number
>
113 BOOST_STATIC_CONSTANT(bool, is_signed
= boost::is_signed
<Number
>::value
);
114 BOOST_STATIC_CONSTANT(Number
, min
=
115 complement_base
<is_signed
>::template values
<Number
>::min
);
116 BOOST_STATIC_CONSTANT(Number
, max
=
117 complement_base
<is_signed
>::template values
<Number
>::max
);
121 // Now here's the "pretty" template you're intended to actually use.
122 // complement_traits<Number>::min, complement_traits<Number>::max are the
123 // minimum and maximum values of Number if Number is a built-in integer type.
124 template <class Number
>
125 struct complement_traits
127 BOOST_STATIC_CONSTANT(Number
, max
= (complement_traits_aux
<Number
, sizeof(Number
)>::max
));
128 BOOST_STATIC_CONSTANT(Number
, min
= (complement_traits_aux
<Number
, sizeof(Number
)>::min
));
131 // =================================================================================
133 // Support for streaming various numeric types in exactly the format I want. I
134 // needed this in addition to all the assertions so that I could see exactly
135 // what was going on.
137 // Numbers go through a 2-stage conversion process (by default, though, no real
140 template <class T
> struct stream_as
{
145 // char types first get converted to unsigned char, then to unsigned.
146 template <> struct stream_as
<char> {
147 typedef unsigned char t1
;
150 template <> struct stream_as
<unsigned char> {
151 typedef unsigned char t1
; typedef unsigned t2
;
153 template <> struct stream_as
<signed char> {
154 typedef unsigned char t1
; typedef unsigned t2
;
157 // C++20 ostream has deleted operator<< for wchar_t
158 template <> struct stream_as
<wchar_t> {
159 typedef unsigned int t1
;
160 typedef unsigned int t2
;
163 #if defined(BOOST_MSVC_STD_ITERATOR) // No intmax streaming built-in
165 // With this library implementation, __int64 and __uint64 get streamed as strings
166 template <> struct stream_as
<boost::uintmax_t> {
167 typedef std::string t1
;
168 typedef std::string t2
;
171 template <> struct stream_as
<boost::intmax_t> {
172 typedef std::string t1
;
173 typedef std::string t2
;
177 // Standard promotion process for streaming
178 template <class T
> struct promote
180 static typename stream_as
<T
>::t1
from(T x
) {
181 typedef typename stream_as
<T
>::t1 t1
;
186 #if defined(BOOST_MSVC_STD_ITERATOR) // No intmax streaming built-in
188 // On this platform, stream them as long/unsigned long if they fit.
189 // Otherwise, write a string.
190 template <> struct promote
<boost::uintmax_t> {
191 std::string
static from(const boost::uintmax_t x
) {
193 return std::string("large unsigned value");
195 std::ostringstream strm
;
196 strm
<< (unsigned long)x
;
201 template <> struct promote
<boost::intmax_t> {
202 std::string
static from(const boost::intmax_t x
) {
203 if (x
> boost::intmax_t(ULONG_MAX
))
204 return std::string("large positive signed value");
206 std::ostringstream strm
;
207 strm
<< (unsigned long)x
;
211 if (x
< boost::intmax_t(LONG_MIN
))
212 return std::string("large negative signed value");
214 std::ostringstream strm
;
222 // This is the function which converts types to the form I want to stream them in.
224 typename stream_as
<T
>::t2
stream_number(T x
)
226 return promote
<T
>::from(x
);
228 // =================================================================================
231 // Tests for built-in signed and unsigned types
234 // Tag types for selecting tests
235 struct unsigned_tag
{};
236 struct signed_tag
{};
238 // Tests for unsigned numbers. The extra default Number parameter works around
240 template <class Number
>
241 void test_aux(unsigned_tag
, Number
*)
243 typedef typename
boost::detail::numeric_traits
<Number
>::difference_type difference_type
;
244 BOOST_STATIC_ASSERT(!boost::is_signed
<Number
>::value
);
246 (sizeof(Number
) < sizeof(boost::intmax_t))
247 | (boost::is_same
<difference_type
, boost::intmax_t>::value
));
249 #if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 0 && __GNUC_PATCHLEVEL__ == 2
250 // GCC 4.0.2 ICEs on this C-style cases
251 BOOST_STATIC_ASSERT((complement_traits
<Number
>::max
) > Number(0));
252 BOOST_STATIC_ASSERT((complement_traits
<Number
>::min
) == Number(0));
254 // Force casting to Number here to work around the fact that it's an enum on MSVC
255 BOOST_STATIC_ASSERT(Number(complement_traits
<Number
>::max
) > Number(0));
256 BOOST_STATIC_ASSERT(Number(complement_traits
<Number
>::min
) == Number(0));
259 const Number max
= complement_traits
<Number
>::max
;
260 const Number min
= complement_traits
<Number
>::min
;
262 const Number test_max
= (sizeof(Number
) < sizeof(boost::intmax_t))
266 std::cout
<< std::hex
<< "(unsigned) min = " << stream_number(min
) << ", max = "
267 << stream_number(max
) << "..." << std::flush
;
268 std::cout
<< "difference_type = " << typeid(difference_type
).name() << "..."
271 difference_type d1
= boost::detail::numeric_distance(Number(0), test_max
);
272 difference_type d2
= boost::detail::numeric_distance(test_max
, Number(0));
274 std::cout
<< "0->" << stream_number(test_max
) << "==" << std::dec
<< stream_number(d1
) << "; "
275 << std::hex
<< stream_number(test_max
) << "->0==" << std::dec
<< stream_number(d2
) << "..." << std::flush
;
277 assert(d1
== difference_type(test_max
));
278 assert(d2
== -difference_type(test_max
));
281 // Tests for signed numbers. The extra default Number parameter works around an
283 struct out_of_range_tag
{};
284 struct in_range_tag
{};
286 // This test morsel gets executed for numbers whose difference will always be
287 // representable in intmax_t
288 template <class Number
>
289 void signed_test(in_range_tag
, Number
*)
291 BOOST_STATIC_ASSERT(boost::is_signed
<Number
>::value
);
292 typedef typename
boost::detail::numeric_traits
<Number
>::difference_type difference_type
;
293 const Number max
= complement_traits
<Number
>::max
;
294 const Number min
= complement_traits
<Number
>::min
;
296 difference_type d1
= boost::detail::numeric_distance(min
, max
);
297 difference_type d2
= boost::detail::numeric_distance(max
, min
);
299 std::cout
<< stream_number(min
) << "->" << stream_number(max
) << "==";
300 std::cout
<< std::dec
<< stream_number(d1
) << "; ";
301 std::cout
<< std::hex
<< stream_number(max
) << "->" << stream_number(min
)
302 << "==" << std::dec
<< stream_number(d2
) << "..." << std::flush
;
303 assert(d1
== difference_type(max
) - difference_type(min
));
304 assert(d2
== difference_type(min
) - difference_type(max
));
307 // This test morsel gets executed for numbers whose difference may exceed the
308 // capacity of intmax_t.
309 template <class Number
>
310 void signed_test(out_of_range_tag
, Number
*)
312 BOOST_STATIC_ASSERT(boost::is_signed
<Number
>::value
);
313 typedef typename
boost::detail::numeric_traits
<Number
>::difference_type difference_type
;
314 const Number max
= complement_traits
<Number
>::max
;
315 const Number min
= complement_traits
<Number
>::min
;
317 difference_type min_distance
= complement_traits
<difference_type
>::min
;
318 difference_type max_distance
= complement_traits
<difference_type
>::max
;
320 const Number n1
= Number(min
+ max_distance
);
321 const Number n2
= Number(max
+ min_distance
);
322 difference_type d1
= boost::detail::numeric_distance(min
, n1
);
323 difference_type d2
= boost::detail::numeric_distance(max
, n2
);
325 std::cout
<< stream_number(min
) << "->" << stream_number(n1
) << "==";
326 std::cout
<< std::dec
<< stream_number(d1
) << "; ";
327 std::cout
<< std::hex
<< stream_number(max
) << "->" << stream_number(n2
)
328 << "==" << std::dec
<< stream_number(d2
) << "..." << std::flush
;
329 assert(d1
== max_distance
);
330 assert(d2
== min_distance
);
333 template <class Number
>
334 void test_aux(signed_tag
, Number
*)
336 typedef typename
boost::detail::numeric_traits
<Number
>::difference_type difference_type
;
337 BOOST_STATIC_ASSERT(boost::is_signed
<Number
>::value
);
339 (sizeof(Number
) < sizeof(boost::intmax_t))
340 | (boost::is_same
<difference_type
, Number
>::value
));
342 #if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 0 && __GNUC_PATCHLEVEL__ == 2
343 // GCC 4.0.2 ICEs on this cast
344 BOOST_STATIC_ASSERT((complement_traits
<Number
>::max
) > Number(0));
345 BOOST_STATIC_ASSERT((complement_traits
<Number
>::min
) < Number(0));
347 // Force casting to Number here to work around the fact that it's an enum on MSVC
348 BOOST_STATIC_ASSERT(Number(complement_traits
<Number
>::max
) > Number(0));
349 BOOST_STATIC_ASSERT(Number(complement_traits
<Number
>::min
) < Number(0));
351 const Number max
= complement_traits
<Number
>::max
;
352 const Number min
= complement_traits
<Number
>::min
;
354 std::cout
<< std::hex
<< "min = " << stream_number(min
) << ", max = "
355 << stream_number(max
) << "..." << std::flush
;
356 std::cout
<< "difference_type = " << typeid(difference_type
).name() << "..."
359 typedef typename
boost::conditional
<
360 (sizeof(Number
) < sizeof(boost::intmax_t)),
364 signed_test
<Number
>(range_tag(), 0);
368 // Test for all numbers. The extra default Number parameter works around an MSVC
370 template <class Number
>
371 void test(Number
* = 0)
373 std::cout
<< "testing " << typeid(Number
).name() << ":\n"
374 #ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
375 << "is_signed: " << (std::numeric_limits
<Number
>::is_signed
? "true\n" : "false\n")
376 << "is_bounded: " << (std::numeric_limits
<Number
>::is_bounded
? "true\n" : "false\n")
377 << "digits: " << std::numeric_limits
<Number
>::digits
<< "\n"
379 << "..." << std::flush
;
381 // factoring out difference_type for the assert below confused Borland :(
382 typedef boost::is_signed
<
383 #if !defined(BOOST_MSVC) || BOOST_MSVC > 1300
386 boost::detail::numeric_traits
<Number
>::difference_type
388 BOOST_STATIC_ASSERT(is_signed::value
);
390 typedef typename
boost::conditional
<
391 boost::is_signed
<Number
>::value
,
396 test_aux
<Number
>(signedness(), 0);
397 std::cout
<< "passed" << std::endl
;
403 test
<unsigned char>();
407 test
<unsigned short>();
409 test
<unsigned int>();
411 test
<unsigned long>();
412 #if defined(BOOST_HAS_LONG_LONG) && !defined(BOOST_NO_INTEGRAL_INT64_T)
413 test
< ::boost::long_long_type
>();
414 test
< ::boost::ulong_long_type
>();
415 #elif defined(BOOST_MSVC)
416 // The problem of not having compile-time static class constants other than
417 // enums prevents this from working, since values get truncated.
418 // test<boost::uintmax_t>();
419 // test<boost::intmax_t>();