1 //
2 //! Copyright (c) 2011
3 //! Brandon Kohn
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See
6 // accompanying file LICENSE_1_0.txt or copy at
7 // http://www.boost.org/LICENSE_1_0.txt)
8 //
9
10 #include <boost/operators.hpp>
11 #include <boost/numeric/conversion/cast.hpp>
12 #include <boost/mpl/for_each.hpp>
13 #include <boost/mpl/vector.hpp>
14 #include <boost/cstdint.hpp>
15 #include <boost/test/minimal.hpp>
16
17 //! Define a simple custom number
18 struct Double
19 {
DoubleDouble20 Double()
21 : v(0)
22 {}
23
24 template <typename T>
DoubleDouble25 explicit Double( T v )
26 : v(static_cast<double>(v))
27 {}
28
29 template <typename T>
operator =Double30 Double& operator= ( T t )
31 {
32 v = static_cast<double>(t);
33 return *this;
34 }
35
operator <Double36 bool operator < ( const Double& rhs ) const
37 {
38 return v < rhs.v;
39 }
40
41 template <typename T>
operator <Double42 bool operator < ( T rhs ) const
43 {
44 return v < static_cast<double>(rhs);
45 }
46
47 template <typename LHS>
operator <(const LHS & lhs,const Double & rhs)48 friend bool operator < ( const LHS& lhs, const Double& rhs )
49 {
50 return lhs < rhs.v;
51 }
52
operator >Double53 bool operator > ( const Double& rhs ) const
54 {
55 return v > rhs.v;
56 }
57
58 template <typename LHS>
operator >(const LHS & lhs,const Double & rhs)59 friend bool operator > ( const LHS& lhs, const Double& rhs )
60 {
61 return lhs > rhs.v;
62 }
63
64 template <typename T>
operator >Double65 bool operator > ( T rhs ) const
66 {
67 return v > static_cast<double>(rhs);
68 }
69
operator ==Double70 bool operator == ( const Double& rhs ) const
71 {
72 return v == rhs.v;
73 }
74
75 template <typename T>
operator ==Double76 bool operator == ( T rhs ) const
77 {
78 return v == static_cast<double>(rhs);
79 }
80
81 template <typename LHS>
operator ==(const LHS & lhs,const Double & rhs)82 friend bool operator == ( const LHS& lhs, const Double& rhs )
83 {
84 return lhs == rhs.v;
85 }
86
operator !Double87 bool operator !() const
88 {
89 return v == 0;
90 }
91
operator -Double92 Double operator -() const
93 {
94 return Double(-v);
95 }
96
operator +=Double97 Double& operator +=( const Double& t )
98 {
99 v += t.v;
100 return *this;
101 }
102
103 template <typename T>
operator +=Double104 Double& operator +=( T t )
105 {
106 v += static_cast<double>(t);
107 return *this;
108 }
109
operator -=Double110 Double& operator -=( const Double& t )
111 {
112 v -= t.v;
113 return *this;
114 }
115
116 template <typename T>
operator -=Double117 Double& operator -=( T t )
118 {
119 v -= static_cast<double>(t);
120 return *this;
121 }
122
operator *=Double123 Double& operator *= ( const Double& factor )
124 {
125 v *= factor.v;
126 return *this;
127 }
128
129 template <typename T>
operator *=Double130 Double& operator *=( T t )
131 {
132 v *= static_cast<double>(t);
133 return *this;
134 }
135
operator /=Double136 Double& operator /= (const Double& divisor)
137 {
138 v /= divisor.v;
139 return *this;
140 }
141
142 template <typename T>
operator /=Double143 Double& operator /=( T t )
144 {
145 v /= static_cast<double>(t);
146 return (*this);
147 }
148
149 double v;
150 };
151
152 //! Define numeric_limits for the custom type.
153 namespace std
154 {
155 template<>
156 class numeric_limits< Double > : public numeric_limits<double>
157 {
158 public:
159
160 //! Limit our Double to a range of +/- 100.0
Double(min)161 static Double (min)()
162 {
163 return Double(1.e-2);
164 }
165
Double(max)166 static Double (max)()
167 {
168 return Double(1.e2);
169 }
170
epsilon()171 static Double epsilon()
172 {
173 return Double( std::numeric_limits<double>::epsilon() );
174 }
175 };
176 }
177
178 //! Define range checking and overflow policies.
179 namespace custom
180 {
181 //! Define a custom range checker
182 template<typename Traits, typename OverFlowHandler>
183 struct range_checker
184 {
185 typedef typename Traits::argument_type argument_type ;
186 typedef typename Traits::source_type S;
187 typedef typename Traits::target_type T;
188
189 //! Check range of integral types.
out_of_rangecustom::range_checker190 static boost::numeric::range_check_result out_of_range( argument_type s )
191 {
192 using namespace boost::numeric;
193 if( s > bounds<T>::highest() )
194 return cPosOverflow;
195 else if( s < bounds<T>::lowest() )
196 return cNegOverflow;
197 else
198 return cInRange;
199 }
200
validate_rangecustom::range_checker201 static void validate_range ( argument_type s )
202 {
203 BOOST_STATIC_ASSERT( std::numeric_limits<T>::is_bounded );
204 OverFlowHandler()( out_of_range(s) );
205 }
206 };
207
208 //! Overflow handler
209 struct positive_overflow{};
210 struct negative_overflow{};
211
212 struct overflow_handler
213 {
operator ()custom::overflow_handler214 void operator() ( boost::numeric::range_check_result r )
215 {
216 using namespace boost::numeric;
217 if( r == cNegOverflow )
218 throw negative_overflow() ;
219 else if( r == cPosOverflow )
220 throw positive_overflow() ;
221 }
222 };
223
224 //! Define a rounding policy and specialize on the custom type.
225 template<class S>
226 struct Ceil : boost::numeric::Ceil<S>{};
227
228 template<>
229 struct Ceil<Double>
230 {
231 typedef Double source_type;
232
233 typedef Double const& argument_type;
234
nearbyintcustom::Ceil235 static source_type nearbyint ( argument_type s )
236 {
237 #if !defined(BOOST_NO_STDC_NAMESPACE)
238 using std::ceil ;
239 #endif
240 return Double( ceil(s.v) );
241 }
242
243 typedef boost::mpl::integral_c< std::float_round_style, std::round_toward_infinity> round_style;
244 };
245
246 //! Define a rounding policy and specialize on the custom type.
247 template<class S>
248 struct Trunc: boost::numeric::Trunc<S>{};
249
250 template<>
251 struct Trunc<Double>
252 {
253 typedef Double source_type;
254
255 typedef Double const& argument_type;
256
nearbyintcustom::Trunc257 static source_type nearbyint ( argument_type s )
258 {
259 #if !defined(BOOST_NO_STDC_NAMESPACE)
260 using std::floor;
261 #endif
262 return Double( floor(s.v) );
263 }
264
265 typedef boost::mpl::integral_c< std::float_round_style, std::round_toward_zero> round_style;
266 };
267 }//namespace custom;
268
269 namespace boost { namespace numeric {
270
271 //! Define the numeric_cast_traits specializations on the custom type.
272 template <typename S>
273 struct numeric_cast_traits<Double, S>
274 {
275 typedef custom::overflow_handler overflow_policy;
276 typedef custom::range_checker
277 <
278 boost::numeric::conversion_traits<Double, S>
279 , overflow_policy
280 > range_checking_policy;
281 typedef boost::numeric::Trunc<S> rounding_policy;
282 };
283
284 template <typename T>
285 struct numeric_cast_traits<T, Double>
286 {
287 typedef custom::overflow_handler overflow_policy;
288 typedef custom::range_checker
289 <
290 boost::numeric::conversion_traits<T, Double>
291 , overflow_policy
292 > range_checking_policy;
293 typedef custom::Trunc<Double> rounding_policy;
294 };
295
296 //! Define the conversion from the custom type to built-in types and vice-versa.
297 template<typename T>
298 struct raw_converter< conversion_traits< T, Double > >
299 {
low_level_convertboost::numeric::raw_converter300 static T low_level_convert ( const Double& n )
301 {
302 return static_cast<T>( n.v );
303 }
304 };
305
306 template<typename S>
307 struct raw_converter< conversion_traits< Double, S > >
308 {
low_level_convertboost::numeric::raw_converter309 static Double low_level_convert ( const S& n )
310 {
311 return Double(n);
312 }
313 };
314 }}//namespace boost::numeric;
315
316 #define BOOST_TEST_CATCH_CUSTOM_POSITIVE_OVERFLOW( CastCode ) \
317 try { CastCode; BOOST_CHECK( false ); } \
318 catch( custom::positive_overflow& ){} \
319 catch(...){ BOOST_CHECK( false ); } \
320 /***/
321
322 #define BOOST_TEST_CATCH_CUSTOM_NEGATIVE_OVERFLOW( CastCode ) \
323 try { CastCode; BOOST_CHECK( false ); } \
324 catch( custom::negative_overflow& ){} \
325 catch(...){ BOOST_CHECK( false ); } \
326 /***/
327
328 struct test_cast_traits
329 {
330 template <typename T>
operator ()test_cast_traits331 void operator()(T) const
332 {
333 Double d = boost::numeric_cast<Double>( static_cast<T>(50) );
334 BOOST_CHECK( d.v == 50. );
335 T v = boost::numeric_cast<T>( d );
336 BOOST_CHECK( v == 50 );
337 }
338 };
339
test_numeric_cast_traits()340 void test_numeric_cast_traits()
341 {
342 typedef boost::mpl::vector
343 <
344 boost::int8_t
345 , boost::uint8_t
346 , boost::int16_t
347 , boost::uint16_t
348 , boost::int32_t
349 , boost::uint32_t
350 #if !defined( BOOST_NO_INT64_T )
351 , boost::int64_t
352 , boost::uint64_t
353 #endif
354 , float
355 , double
356 , long double
357 > types;
358 boost::mpl::for_each<types>( test_cast_traits() );
359
360 //! Check overflow handler.
361 Double d( 56.0 );
362 BOOST_TEST_CATCH_CUSTOM_POSITIVE_OVERFLOW( d = boost::numeric_cast<Double>( 101 ) );
363 BOOST_CHECK( d.v == 56. );
364 BOOST_TEST_CATCH_CUSTOM_NEGATIVE_OVERFLOW( d = boost::numeric_cast<Double>( -101 ) );
365 BOOST_CHECK( d.v == 56.);
366
367 //! Check custom round policy.
368 d = 5.9;
369 int five = boost::numeric_cast<int>( d );
370 BOOST_CHECK( five == 5 );
371 }
372
test_main(int argc,char * argv[])373 int test_main( int argc, char * argv[] )
374 {
375 test_numeric_cast_traits();
376 return 0;
377 }
378
379 #undef BOOST_TEST_CATCH_CUSTOM_POSITIVE_OVERFLOW
380 #undef BOOST_TEST_CATCH_CUSTOM_NEGATIVE_OVERFLOW
381