| 1 | // © Copyright Fernando Luis Cacciola Carballal 2000-2004 |
|---|
| 2 | // Use, modification, and distribution is subject to the Boost Software |
|---|
| 3 | // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at |
|---|
| 4 | // http://www.boost.org/LICENSE_1_0.txt) |
|---|
| 5 | |
|---|
| 6 | // See library home page at http://www.boost.org/libs/numeric/conversion |
|---|
| 7 | // |
|---|
| 8 | // Contact the author at: fernando_cacciola@hotmail.com |
|---|
| 9 | // |
|---|
| 10 | #include<cstdlib> |
|---|
| 11 | #include<iostream> |
|---|
| 12 | #include<iomanip> |
|---|
| 13 | #include<string> |
|---|
| 14 | #include<typeinfo> |
|---|
| 15 | #include<vector> |
|---|
| 16 | #include<algorithm> |
|---|
| 17 | |
|---|
| 18 | #include "boost/config.hpp" |
|---|
| 19 | #include "boost/cstdint.hpp" |
|---|
| 20 | #include "boost/utility.hpp" |
|---|
| 21 | |
|---|
| 22 | // |
|---|
| 23 | // Borland 5.5 lacks the following math overloads |
|---|
| 24 | // |
|---|
| 25 | #if BOOST_WORKAROUND(__BORLANDC__, <= 0x551) |
|---|
| 26 | namespace std |
|---|
| 27 | { |
|---|
| 28 | |
|---|
| 29 | inline float ceil (float x) { return std::ceil ( static_cast<double>(x)); } |
|---|
| 30 | inline float floor (float x) { return std::floor ( static_cast<double>(x)); } |
|---|
| 31 | inline long double ceil (long double x) { return std::ceill (x); } |
|---|
| 32 | inline long double floor (long double x) { return std::floorl(x); } |
|---|
| 33 | |
|---|
| 34 | } // namespace std |
|---|
| 35 | #endif |
|---|
| 36 | |
|---|
| 37 | #include "boost/numeric/conversion/converter.hpp" |
|---|
| 38 | #include "boost/numeric/conversion/cast.hpp" |
|---|
| 39 | |
|---|
| 40 | #ifdef __BORLANDC__ |
|---|
| 41 | #pragma hdrstop |
|---|
| 42 | #endif |
|---|
| 43 | |
|---|
| 44 | #include "test_helpers.cpp" |
|---|
| 45 | #include "test_helpers2.cpp" |
|---|
| 46 | #include "test_helpers3.cpp" |
|---|
| 47 | |
|---|
| 48 | #include "boost/mpl/alias.hpp" |
|---|
| 49 | |
|---|
| 50 | using std::cout ; |
|---|
| 51 | |
|---|
| 52 | // A generic 'abs' function. |
|---|
| 53 | template<class N> inline N absG ( N v ) |
|---|
| 54 | { |
|---|
| 55 | return v < static_cast<N>(0) ? static_cast<N>(-v) : v ; |
|---|
| 56 | } |
|---|
| 57 | template<> inline unsigned char absG<unsigned char> ( unsigned char v ) { return v ; } |
|---|
| 58 | template<> inline unsigned short absG<unsigned short> ( unsigned short v ) { return v ; } |
|---|
| 59 | template<> inline unsigned int absG<unsigned int> ( unsigned int v ) { return v ; } |
|---|
| 60 | template<> inline unsigned long absG<unsigned long> ( unsigned long v ) { return v ; } |
|---|
| 61 | |
|---|
| 62 | template<class T> inline void unused_variable ( T const& ) {} |
|---|
| 63 | // |
|---|
| 64 | // The following function excersizes specific conversions that cover |
|---|
| 65 | // usual and boundary cases for each relevant combination. |
|---|
| 66 | // |
|---|
| 67 | void test_conversions() |
|---|
| 68 | { |
|---|
| 69 | using namespace boost ; |
|---|
| 70 | using namespace numeric ; |
|---|
| 71 | |
|---|
| 72 | // To help the test found possible bugs a random numbers are used. |
|---|
| 73 | #if !defined(BOOST_NO_STDC_NAMESPACE) |
|---|
| 74 | using std::rand ; |
|---|
| 75 | #endif |
|---|
| 76 | |
|---|
| 77 | boost::int16_t v16 ; |
|---|
| 78 | boost::uint16_t uv16 ; |
|---|
| 79 | boost::int32_t v32 ; |
|---|
| 80 | boost::uint32_t uv32 ; |
|---|
| 81 | |
|---|
| 82 | volatile float fv ; // avoid this to be cached internally in some fpu register |
|---|
| 83 | volatile double dv ; // avoid this to be cached internally in some fpu register |
|---|
| 84 | |
|---|
| 85 | // |
|---|
| 86 | // sample (representative) conversions: |
|---|
| 87 | // |
|---|
| 88 | cout << "Testing representative conversions\n"; |
|---|
| 89 | |
|---|
| 90 | // integral to integral |
|---|
| 91 | |
|---|
| 92 | // signed to signed |
|---|
| 93 | |
|---|
| 94 | // not subranged |
|---|
| 95 | v16 = static_cast<boost::int16_t>(rand()); |
|---|
| 96 | TEST_SUCCEEDING_CONVERSION_DEF(boost::int32_t,boost::int16_t,v16,v16); |
|---|
| 97 | |
|---|
| 98 | // subranged |
|---|
| 99 | v16 = static_cast<boost::int16_t>(rand()); |
|---|
| 100 | TEST_SUCCEEDING_CONVERSION_DEF(boost::int16_t,boost::int32_t,v16,v16); |
|---|
| 101 | TEST_POS_OVERFLOW_CONVERSION_DEF(boost::int16_t,boost::int32_t,bounds<boost::int16_t>::highest() + boost::int32_t(1) ) ; |
|---|
| 102 | TEST_NEG_OVERFLOW_CONVERSION_DEF(boost::int16_t,boost::int32_t,bounds<boost::int16_t>::lowest() - boost::int32_t(1) ) ; |
|---|
| 103 | |
|---|
| 104 | // signed to unsigned |
|---|
| 105 | |
|---|
| 106 | // subranged |
|---|
| 107 | v32 = absG(static_cast<boost::int32_t>(rand())); |
|---|
| 108 | v16 = absG(static_cast<boost::int16_t>(rand())); |
|---|
| 109 | TEST_SUCCEEDING_CONVERSION_DEF(boost::uint32_t,boost::int32_t,v32,v32); |
|---|
| 110 | TEST_SUCCEEDING_CONVERSION_DEF(boost::uint16_t,boost::int32_t,v16,v16); |
|---|
| 111 | TEST_POS_OVERFLOW_CONVERSION_DEF(boost::uint16_t,boost::int32_t,bounds<boost::uint16_t>::highest() + boost::int32_t(1) ) ; |
|---|
| 112 | TEST_NEG_OVERFLOW_CONVERSION_DEF(boost::uint32_t,boost::int32_t,boost::int32_t(-1) ) ; |
|---|
| 113 | |
|---|
| 114 | // unsigned to signed |
|---|
| 115 | |
|---|
| 116 | // not subranged |
|---|
| 117 | v32 = absG(static_cast<boost::int32_t>(rand())); |
|---|
| 118 | TEST_SUCCEEDING_CONVERSION_DEF(boost::int32_t,boost::uint32_t,v32,v32); |
|---|
| 119 | |
|---|
| 120 | // subranged |
|---|
| 121 | v16 = absG(static_cast<boost::int16_t>(rand())); |
|---|
| 122 | TEST_SUCCEEDING_CONVERSION_DEF(boost::int16_t,boost::uint32_t,v16,v16); |
|---|
| 123 | TEST_POS_OVERFLOW_CONVERSION_DEF(boost::int32_t,boost::uint32_t,bounds<boost::uint32_t>::highest() ) ; |
|---|
| 124 | TEST_POS_OVERFLOW_CONVERSION_DEF(boost::int16_t,boost::uint32_t,bounds<boost::uint32_t>::highest() ) ; |
|---|
| 125 | |
|---|
| 126 | // unsigned to unsigned |
|---|
| 127 | |
|---|
| 128 | // not subranged |
|---|
| 129 | uv16 = static_cast<boost::uint16_t>(rand()); |
|---|
| 130 | TEST_SUCCEEDING_CONVERSION_DEF(boost::uint32_t,boost::uint16_t,uv16,uv16); |
|---|
| 131 | |
|---|
| 132 | // subranged |
|---|
| 133 | uv16 = static_cast<boost::uint16_t>(rand()); |
|---|
| 134 | TEST_SUCCEEDING_CONVERSION_DEF(boost::uint16_t,boost::uint32_t,uv16,uv16); |
|---|
| 135 | TEST_POS_OVERFLOW_CONVERSION_DEF(boost::uint16_t,boost::uint32_t,bounds<boost::uint32_t>::highest() ) ; |
|---|
| 136 | |
|---|
| 137 | // integral to float |
|---|
| 138 | |
|---|
| 139 | // from signed integral |
|---|
| 140 | v32 = static_cast<boost::int32_t>(rand()); |
|---|
| 141 | TEST_SUCCEEDING_CONVERSION_DEF(double,boost::int32_t,v32,v32); |
|---|
| 142 | |
|---|
| 143 | // from uint32_tegral |
|---|
| 144 | uv32 = static_cast<boost::uint32_t>(rand()); |
|---|
| 145 | TEST_SUCCEEDING_CONVERSION_DEF(double,boost::uint32_t,uv32,uv32); |
|---|
| 146 | |
|---|
| 147 | // float to integral |
|---|
| 148 | |
|---|
| 149 | // to signed integral |
|---|
| 150 | v32 = static_cast<boost::int32_t>(rand()); |
|---|
| 151 | TEST_SUCCEEDING_CONVERSION_DEF(boost::int32_t,double,v32,v32); |
|---|
| 152 | |
|---|
| 153 | dv = static_cast<double>(bounds<boost::uint32_t>::highest()) + 1.0 ; |
|---|
| 154 | TEST_POS_OVERFLOW_CONVERSION_DEF(boost::int32_t,double,dv) ; |
|---|
| 155 | TEST_NEG_OVERFLOW_CONVERSION_DEF(boost::int32_t,double,-dv) ; |
|---|
| 156 | |
|---|
| 157 | // float to float |
|---|
| 158 | |
|---|
| 159 | // not subranged |
|---|
| 160 | fv = static_cast<float>(rand()) / static_cast<float>(3) ; |
|---|
| 161 | TEST_SUCCEEDING_CONVERSION_DEF(double,float,fv,fv); |
|---|
| 162 | |
|---|
| 163 | |
|---|
| 164 | // subranged |
|---|
| 165 | fv = static_cast<float>(rand()) / static_cast<float>(3) ; |
|---|
| 166 | TEST_SUCCEEDING_CONVERSION_DEF(float,double,fv,fv); |
|---|
| 167 | TEST_POS_OVERFLOW_CONVERSION_DEF(float,double,bounds<double>::highest()) ; |
|---|
| 168 | TEST_NEG_OVERFLOW_CONVERSION_DEF(float,double,bounds<double>::lowest ()) ; |
|---|
| 169 | } |
|---|
| 170 | |
|---|
| 171 | // Custom OverflowHandler |
|---|
| 172 | struct custom_overflow_handler |
|---|
| 173 | { |
|---|
| 174 | void operator() ( boost::numeric::range_check_result r ) |
|---|
| 175 | { |
|---|
| 176 | if ( r == boost::numeric::cNegOverflow ) |
|---|
| 177 | cout << "negative_overflow detected!\n" ; |
|---|
| 178 | else if ( r == boost::numeric::cPosOverflow ) |
|---|
| 179 | cout << "positive_overflow detected!\n" ; |
|---|
| 180 | } |
|---|
| 181 | } ; |
|---|
| 182 | |
|---|
| 183 | template<class T, class S,class OverflowHandler> |
|---|
| 184 | void test_overflow_handler( MATCH_FNTPL_ARG(T), MATCH_FNTPL_ARG(S), MATCH_FNTPL_ARG(OverflowHandler), |
|---|
| 185 | PostCondition pos, |
|---|
| 186 | PostCondition neg |
|---|
| 187 | ) |
|---|
| 188 | { |
|---|
| 189 | typedef boost::numeric::conversion_traits<T,S> traits ; |
|---|
| 190 | typedef boost::numeric::converter<T,S,traits,OverflowHandler> converter ; |
|---|
| 191 | |
|---|
| 192 | static const S psrc = boost::numeric::bounds<S>::highest(); |
|---|
| 193 | static const S nsrc = boost::numeric::bounds<S>::lowest (); |
|---|
| 194 | |
|---|
| 195 | static const T pres = static_cast<T>(psrc); |
|---|
| 196 | static const T nres = static_cast<T>(nsrc); |
|---|
| 197 | |
|---|
| 198 | test_conv_base ( ConversionInstance<converter>(pres,psrc,pos) ) ; |
|---|
| 199 | test_conv_base ( ConversionInstance<converter>(nres,nsrc,neg) ) ; |
|---|
| 200 | } |
|---|
| 201 | |
|---|
| 202 | template<class T, class S> |
|---|
| 203 | void test_overflow_handlers( MATCH_FNTPL_ARG(T), MATCH_FNTPL_ARG(S) ) |
|---|
| 204 | { |
|---|
| 205 | cout << "Testing Silent Overflow Handler policy\n"; |
|---|
| 206 | |
|---|
| 207 | test_overflow_handler( SET_FNTPL_ARG(T), |
|---|
| 208 | SET_FNTPL_ARG(S), |
|---|
| 209 | SET_FNTPL_ARG(boost::numeric::silent_overflow_handler), |
|---|
| 210 | c_converted, |
|---|
| 211 | c_converted |
|---|
| 212 | ) ; |
|---|
| 213 | |
|---|
| 214 | cout << "Testing Default Overflow Handler policy\n"; |
|---|
| 215 | |
|---|
| 216 | test_overflow_handler( SET_FNTPL_ARG(T), |
|---|
| 217 | SET_FNTPL_ARG(S), |
|---|
| 218 | SET_FNTPL_ARG(boost::numeric::def_overflow_handler), |
|---|
| 219 | c_pos_overflow, |
|---|
| 220 | c_neg_overflow |
|---|
| 221 | ) ; |
|---|
| 222 | |
|---|
| 223 | cout << "Testing Custom (User-Defined) Overflow Handler policy\n"; |
|---|
| 224 | |
|---|
| 225 | test_overflow_handler( SET_FNTPL_ARG(T), |
|---|
| 226 | SET_FNTPL_ARG(S), |
|---|
| 227 | SET_FNTPL_ARG(custom_overflow_handler), |
|---|
| 228 | c_converted, |
|---|
| 229 | c_converted |
|---|
| 230 | ) ; |
|---|
| 231 | } |
|---|
| 232 | |
|---|
| 233 | // For a given float-type number 'n' of integer value (n.0), check the conversions |
|---|
| 234 | // within the range [n-1,n+1] taking values at: (n-1,n-0.5,n,n+0.5,n+1). |
|---|
| 235 | // For each sampled value there is an expected result and a PostCondition according to the |
|---|
| 236 | // specified round_style. |
|---|
| 237 | // |
|---|
| 238 | template<class T, class S, class Float2IntRounder> |
|---|
| 239 | void test_rounding_conversion ( MATCH_FNTPL_ARG(T), MATCH_FNTPL_ARG(Float2IntRounder), |
|---|
| 240 | S s, |
|---|
| 241 | PostCondition resl1, |
|---|
| 242 | PostCondition resl0, |
|---|
| 243 | PostCondition res, |
|---|
| 244 | PostCondition resr0, |
|---|
| 245 | PostCondition resr1 |
|---|
| 246 | ) |
|---|
| 247 | { |
|---|
| 248 | typedef boost::numeric::conversion_traits<T,S> Traits ; |
|---|
| 249 | |
|---|
| 250 | typedef boost::numeric::converter<T,S, Traits, boost::numeric::def_overflow_handler,Float2IntRounder> |
|---|
| 251 | Converter ; |
|---|
| 252 | |
|---|
| 253 | S sl1 = s - static_cast<S>(1); |
|---|
| 254 | S sl0 = s - static_cast<S>(0.5); |
|---|
| 255 | S sr0 = s + static_cast<S>(0.5); |
|---|
| 256 | S sr1 = s + static_cast<S>(1); |
|---|
| 257 | |
|---|
| 258 | T tl1 = static_cast<T>( Converter::nearbyint(sl1) ); |
|---|
| 259 | T tl0 = static_cast<T>( Converter::nearbyint(sl0) ); |
|---|
| 260 | T t = static_cast<T>( Converter::nearbyint(s) ); |
|---|
| 261 | T tr0 = static_cast<T>( Converter::nearbyint(sr0) ); |
|---|
| 262 | T tr1 = static_cast<T>( Converter::nearbyint(sr1) ); |
|---|
| 263 | |
|---|
| 264 | test_conv_base ( ConversionInstance<Converter>(tl1,sl1,resl1) ) ; |
|---|
| 265 | test_conv_base ( ConversionInstance<Converter>(tl0,sl0,resl0) ) ; |
|---|
| 266 | test_conv_base ( ConversionInstance<Converter>(t,s,res) ) ; |
|---|
| 267 | test_conv_base ( ConversionInstance<Converter>(tr0,sr0,resr0) ) ; |
|---|
| 268 | test_conv_base ( ConversionInstance<Converter>(tr1,sr1,resr1) ) ; |
|---|
| 269 | } |
|---|
| 270 | |
|---|
| 271 | |
|---|
| 272 | template<class T,class S> |
|---|
| 273 | void test_round_style( MATCH_FNTPL_ARG(T), MATCH_FNTPL_ARG(S) ) |
|---|
| 274 | { |
|---|
| 275 | S min = boost::numeric::bounds<T>::lowest(); |
|---|
| 276 | S max = boost::numeric::bounds<T>::highest(); |
|---|
| 277 | |
|---|
| 278 | cout << "Testing 'Trunc' Float2IntRounder policy\n"; |
|---|
| 279 | |
|---|
| 280 | test_rounding_conversion(SET_FNTPL_ARG(T), |
|---|
| 281 | SET_FNTPL_ARG(boost::numeric::Trunc<S>), |
|---|
| 282 | min, |
|---|
| 283 | c_neg_overflow, |
|---|
| 284 | c_converted, |
|---|
| 285 | c_converted, |
|---|
| 286 | c_converted, |
|---|
| 287 | c_converted |
|---|
| 288 | ) ; |
|---|
| 289 | |
|---|
| 290 | test_rounding_conversion(SET_FNTPL_ARG(T), |
|---|
| 291 | SET_FNTPL_ARG(boost::numeric::Trunc<S>), |
|---|
| 292 | max, |
|---|
| 293 | c_converted, |
|---|
| 294 | c_converted, |
|---|
| 295 | c_converted, |
|---|
| 296 | c_converted, |
|---|
| 297 | c_pos_overflow |
|---|
| 298 | ) ; |
|---|
| 299 | |
|---|
| 300 | cout << "Testing 'RoundEven' Float2IntRounder policy\n"; |
|---|
| 301 | |
|---|
| 302 | test_rounding_conversion(SET_FNTPL_ARG(T), |
|---|
| 303 | SET_FNTPL_ARG(boost::numeric::RoundEven<S>), |
|---|
| 304 | min, |
|---|
| 305 | c_neg_overflow, |
|---|
| 306 | c_converted, |
|---|
| 307 | c_converted, |
|---|
| 308 | c_converted, |
|---|
| 309 | c_converted |
|---|
| 310 | ) ; |
|---|
| 311 | |
|---|
| 312 | test_rounding_conversion(SET_FNTPL_ARG(T), |
|---|
| 313 | SET_FNTPL_ARG(boost::numeric::RoundEven<S>), |
|---|
| 314 | max, |
|---|
| 315 | c_converted, |
|---|
| 316 | c_converted, |
|---|
| 317 | c_converted, |
|---|
| 318 | c_pos_overflow, |
|---|
| 319 | c_pos_overflow |
|---|
| 320 | ) ; |
|---|
| 321 | |
|---|
| 322 | cout << "Testing 'Ceil' Float2IntRounder policy\n"; |
|---|
| 323 | |
|---|
| 324 | test_rounding_conversion(SET_FNTPL_ARG(T), |
|---|
| 325 | SET_FNTPL_ARG(boost::numeric::Ceil<S>), |
|---|
| 326 | min, |
|---|
| 327 | c_neg_overflow, |
|---|
| 328 | c_converted, |
|---|
| 329 | c_converted, |
|---|
| 330 | c_converted, |
|---|
| 331 | c_converted |
|---|
| 332 | ) ; |
|---|
| 333 | |
|---|
| 334 | test_rounding_conversion(SET_FNTPL_ARG(T), |
|---|
| 335 | SET_FNTPL_ARG(boost::numeric::Ceil<S>), |
|---|
| 336 | max, |
|---|
| 337 | c_converted, |
|---|
| 338 | c_converted, |
|---|
| 339 | c_converted, |
|---|
| 340 | c_pos_overflow, |
|---|
| 341 | c_pos_overflow |
|---|
| 342 | ) ; |
|---|
| 343 | |
|---|
| 344 | cout << "Testing 'Floor' Float2IntRounder policy\n" ; |
|---|
| 345 | |
|---|
| 346 | test_rounding_conversion(SET_FNTPL_ARG(T), |
|---|
| 347 | SET_FNTPL_ARG(boost::numeric::Floor<S>), |
|---|
| 348 | min, |
|---|
| 349 | c_neg_overflow, |
|---|
| 350 | c_neg_overflow, |
|---|
| 351 | c_converted, |
|---|
| 352 | c_converted, |
|---|
| 353 | c_converted |
|---|
| 354 | ) ; |
|---|
| 355 | |
|---|
| 356 | test_rounding_conversion(SET_FNTPL_ARG(T), |
|---|
| 357 | SET_FNTPL_ARG(boost::numeric::Floor<S>), |
|---|
| 358 | max, |
|---|
| 359 | c_converted, |
|---|
| 360 | c_converted, |
|---|
| 361 | c_converted, |
|---|
| 362 | c_converted, |
|---|
| 363 | c_pos_overflow |
|---|
| 364 | ) ; |
|---|
| 365 | |
|---|
| 366 | } |
|---|
| 367 | |
|---|
| 368 | void test_round_even( double n, double x ) |
|---|
| 369 | { |
|---|
| 370 | double r = boost::numeric::RoundEven<double>::nearbyint(n); |
|---|
| 371 | BOOST_CHECK( r == x ) ; |
|---|
| 372 | } |
|---|
| 373 | |
|---|
| 374 | void test_round_even() |
|---|
| 375 | { |
|---|
| 376 | cout << "Testing 'RoundEven' tie-breaking\n"; |
|---|
| 377 | |
|---|
| 378 | double min = boost::numeric::bounds<double>::lowest(); |
|---|
| 379 | double max = boost::numeric::bounds<double>::highest(); |
|---|
| 380 | |
|---|
| 381 | #if !defined(BOOST_NO_STDC_NAMESPACE) |
|---|
| 382 | using std::floor ; |
|---|
| 383 | using std::ceil ; |
|---|
| 384 | #endif |
|---|
| 385 | test_round_even(min, floor(min)); |
|---|
| 386 | test_round_even(max, ceil (max)); |
|---|
| 387 | test_round_even(2.0, 2.0); |
|---|
| 388 | test_round_even(2.3, 2.0); |
|---|
| 389 | test_round_even(2.5, 2.0); |
|---|
| 390 | test_round_even(2.7, 3.0); |
|---|
| 391 | test_round_even(3.0, 3.0); |
|---|
| 392 | test_round_even(3.3, 3.0); |
|---|
| 393 | test_round_even(3.5, 4.0); |
|---|
| 394 | test_round_even(3.7, 4.0); |
|---|
| 395 | } |
|---|
| 396 | |
|---|
| 397 | int double_to_int ( double n ) { return static_cast<int>(n) ; } |
|---|
| 398 | |
|---|
| 399 | void test_converter_as_function_object() |
|---|
| 400 | { |
|---|
| 401 | cout << "Testing converter as function object.\n"; |
|---|
| 402 | |
|---|
| 403 | // Create a sample sequence of double values. |
|---|
| 404 | std::vector<double> S ; |
|---|
| 405 | for ( int i = 0 ; i < 10 ; ++ i ) |
|---|
| 406 | S.push_back( i * ( 18.0 / 19.0 ) ); |
|---|
| 407 | |
|---|
| 408 | // Create a sequence of int values from 's' using the standard conversion. |
|---|
| 409 | std::vector<int> W ; |
|---|
| 410 | std::transform(S.begin(),S.end(),std::back_inserter(W),double_to_int); |
|---|
| 411 | |
|---|
| 412 | // Create a sequence of int values from s using a default numeric::converter |
|---|
| 413 | std::vector<int> I ; |
|---|
| 414 | std::transform(S.begin(), |
|---|
| 415 | S.end(), |
|---|
| 416 | std::back_inserter(I), |
|---|
| 417 | boost::numeric::converter<int,double>() |
|---|
| 418 | ) ; |
|---|
| 419 | |
|---|
| 420 | // Match 'w' and 'i' which should be equal. |
|---|
| 421 | bool double_to_int_OK = std::equal(W.begin(),W.end(),I.begin()) ; |
|---|
| 422 | BOOST_CHECK_MESSAGE(double_to_int_OK, "converter (int,double) as function object"); |
|---|
| 423 | |
|---|
| 424 | // Create a sequence of double values from s using a default numeric::converter (which should be the trivial conv). |
|---|
| 425 | std::vector<double> D ; |
|---|
| 426 | std::transform(S.begin(), |
|---|
| 427 | S.end(), |
|---|
| 428 | std::back_inserter(D), |
|---|
| 429 | boost::numeric::converter<double,double>() |
|---|
| 430 | ) ; |
|---|
| 431 | |
|---|
| 432 | // Match 's' and 'd' which should be equal. |
|---|
| 433 | bool double_to_double_OK = std::equal(S.begin(),S.end(),D.begin()) ; |
|---|
| 434 | BOOST_CHECK_MESSAGE(double_to_double_OK, "converter (double,double) as function object"); |
|---|
| 435 | } |
|---|
| 436 | |
|---|
| 437 | #if BOOST_WORKAROUND(__IBMCPP__, <= 600 ) // VCAPP6 |
|---|
| 438 | # define UNOPTIMIZED |
|---|
| 439 | #else |
|---|
| 440 | # define UNOPTIMIZED volatile |
|---|
| 441 | #endif |
|---|
| 442 | |
|---|
| 443 | void test_optimizations() |
|---|
| 444 | { |
|---|
| 445 | using namespace boost; |
|---|
| 446 | using namespace numeric; |
|---|
| 447 | |
|---|
| 448 | float fv0 = 18.0f / 19.0f ; |
|---|
| 449 | |
|---|
| 450 | // This code deosn't produce any output. |
|---|
| 451 | // It is intended to show the optimization of numeric::converter<> by manual inspection |
|---|
| 452 | // of the generated code. |
|---|
| 453 | // Each test shows first the equivalent hand-coded version. |
|---|
| 454 | // The numeric_cast<> code should be the same if full compiler optimization/inlining is used. |
|---|
| 455 | |
|---|
| 456 | //--------------------------------- |
|---|
| 457 | // trivial conversion. |
|---|
| 458 | // |
|---|
| 459 | // equivalent code: |
|---|
| 460 | UNOPTIMIZED float fv1a = fv0 ; |
|---|
| 461 | |
|---|
| 462 | float fv1b = numeric_cast<float>(fv0); |
|---|
| 463 | unused_variable(fv1a); |
|---|
| 464 | unused_variable(fv1b); |
|---|
| 465 | // |
|---|
| 466 | //--------------------------------- |
|---|
| 467 | |
|---|
| 468 | //--------------------------------- |
|---|
| 469 | // nonsubranged conversion. |
|---|
| 470 | // |
|---|
| 471 | // equivalent code: |
|---|
| 472 | UNOPTIMIZED double dv1a = static_cast<double>(fv0); |
|---|
| 473 | |
|---|
| 474 | double dv1b = numeric_cast<double>(fv0); |
|---|
| 475 | unused_variable(dv1a); |
|---|
| 476 | unused_variable(dv1b); |
|---|
| 477 | // |
|---|
| 478 | //--------------------------------- |
|---|
| 479 | |
|---|
| 480 | //------------------------------------------------------ |
|---|
| 481 | // subranged conversion with both-sided range checking. |
|---|
| 482 | // |
|---|
| 483 | |
|---|
| 484 | // equivalent code: |
|---|
| 485 | |
|---|
| 486 | { |
|---|
| 487 | double const& s = dv1b ; |
|---|
| 488 | // range checking |
|---|
| 489 | range_check_result r = s < static_cast<double>(bounds<float>::lowest()) |
|---|
| 490 | ? cNegOverflow : cInRange ; |
|---|
| 491 | if ( r == cInRange ) |
|---|
| 492 | { |
|---|
| 493 | r = s > static_cast<double>(bounds<float>::highest()) ? cPosOverflow : cInRange ; |
|---|
| 494 | } |
|---|
| 495 | if ( r == cNegOverflow ) |
|---|
| 496 | throw negative_overflow() ; |
|---|
| 497 | else if ( r == cPosOverflow ) |
|---|
| 498 | throw positive_overflow() ; |
|---|
| 499 | // conversion |
|---|
| 500 | UNOPTIMIZED float fv2a = static_cast<float>(s); |
|---|
| 501 | unused_variable(fv2a); |
|---|
| 502 | } |
|---|
| 503 | |
|---|
| 504 | float fv2b = numeric_cast<float>(dv1b); |
|---|
| 505 | unused_variable(fv2b); |
|---|
| 506 | // |
|---|
| 507 | //--------------------------------- |
|---|
| 508 | |
|---|
| 509 | |
|---|
| 510 | //--------------------------------- |
|---|
| 511 | // subranged rounding conversion |
|---|
| 512 | // |
|---|
| 513 | // equivalent code: |
|---|
| 514 | |
|---|
| 515 | { |
|---|
| 516 | double const& s = dv1b ; |
|---|
| 517 | // range checking |
|---|
| 518 | range_check_result r = s <= static_cast<double>(bounds<int>::lowest()) - static_cast<double>(1.0) |
|---|
| 519 | ? cNegOverflow : cInRange ; |
|---|
| 520 | if ( r == cInRange ) |
|---|
| 521 | { |
|---|
| 522 | r = s >= static_cast<double>(bounds<int>::highest()) + static_cast<double>(1.0) |
|---|
| 523 | ? cPosOverflow : cInRange ; |
|---|
| 524 | } |
|---|
| 525 | if ( r == cNegOverflow ) |
|---|
| 526 | throw negative_overflow() ; |
|---|
| 527 | else if ( r == cPosOverflow ) |
|---|
| 528 | throw positive_overflow() ; |
|---|
| 529 | // rounding |
|---|
| 530 | |
|---|
| 531 | #if !defined(BOOST_NO_STDC_NAMESPACE) |
|---|
| 532 | using std::floor ; |
|---|
| 533 | #endif |
|---|
| 534 | |
|---|
| 535 | double s1 = floor(dv1b + 0.5); |
|---|
| 536 | |
|---|
| 537 | // conversion |
|---|
| 538 | UNOPTIMIZED int iv1a = static_cast<int>(s1); |
|---|
| 539 | unused_variable(iv1a); |
|---|
| 540 | } |
|---|
| 541 | |
|---|
| 542 | int iv1b = numeric_cast<int>(dv1b); |
|---|
| 543 | unused_variable(iv1b); |
|---|
| 544 | // |
|---|
| 545 | //--------------------------------- |
|---|
| 546 | } |
|---|
| 547 | |
|---|
| 548 | int test_main( int, char* argv[] ) |
|---|
| 549 | { |
|---|
| 550 | std::cout << std::setprecision( std::numeric_limits<long double>::digits10 ) ; |
|---|
| 551 | |
|---|
| 552 | test_conversions(); |
|---|
| 553 | test_overflow_handlers( SET_FNTPL_ARG(boost::int16_t), SET_FNTPL_ARG(boost::int32_t)); |
|---|
| 554 | test_round_style(SET_FNTPL_ARG(boost::int32_t), SET_FNTPL_ARG(double) ) ; |
|---|
| 555 | test_round_even() ; |
|---|
| 556 | test_converter_as_function_object(); |
|---|
| 557 | test_optimizations() ; |
|---|
| 558 | |
|---|
| 559 | return 0; |
|---|
| 560 | } |
|---|
| 561 | //--------------------------------------------------------------------------- |
|---|
| 562 | |
|---|