1 | // (c) 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 | #ifndef BOOST_NUMERIC_CONVERSION_DETAIL_CONVERTER_FLC_12NOV2002_HPP |
11 | #define BOOST_NUMERIC_CONVERSION_DETAIL_CONVERTER_FLC_12NOV2002_HPP |
12 | |
13 | #include <functional> |
14 | |
15 | #include "boost/numeric/conversion/detail/meta.hpp" |
16 | #include "boost/numeric/conversion/detail/conversion_traits.hpp" |
17 | #include "boost/numeric/conversion/bounds.hpp" |
18 | |
19 | #include "boost/type_traits/is_same.hpp" |
20 | |
21 | #include "boost/mpl/integral_c.hpp" |
22 | |
23 | namespace boost { namespace numeric { namespace convdetail |
24 | { |
25 | // Integral Constants representing rounding modes |
26 | typedef mpl::integral_c<std::float_round_style, std::round_toward_zero> round2zero_c ; |
27 | typedef mpl::integral_c<std::float_round_style, std::round_to_nearest> round2nearest_c ; |
28 | typedef mpl::integral_c<std::float_round_style, std::round_toward_infinity> round2inf_c ; |
29 | typedef mpl::integral_c<std::float_round_style, std::round_toward_neg_infinity> round2neg_inf_c ; |
30 | |
31 | // Metafunction: |
32 | // |
33 | // for_round_style<RoundStyle,RoundToZero,RoundToNearest,RoundToInf,RoundToNegInf>::type |
34 | // |
35 | // {RoundStyle} Integral Constant specifying a round style as declared above. |
36 | // {RoundToZero,RoundToNearest,RoundToInf,RoundToNegInf} arbitrary types. |
37 | // |
38 | // Selects one of the 4 types according to the value of RoundStyle. |
39 | // |
40 | template<class RoundStyle,class RoundToZero,class RoundToNearest,class RoundToInf,class RoundToNegInf> |
41 | struct for_round_style |
42 | { |
43 | typedef ct_switch4<RoundStyle |
44 | , round2zero_c, round2nearest_c, round2inf_c // round2neg_inf_c |
45 | , RoundToZero , RoundToNearest , RoundToInf , RoundToNegInf |
46 | > selector ; |
47 | |
48 | typedef typename selector::type type ; |
49 | } ; |
50 | |
51 | |
52 | |
53 | |
54 | |
55 | |
56 | |
57 | |
58 | |
59 | |
60 | |
61 | |
62 | |
63 | |
64 | |
65 | |
66 | |
67 | |
68 | //-------------------------------------------------------------------------- |
69 | // Range Checking Logic. |
70 | // |
71 | // The range checking logic is built up by combining 1 or 2 predicates. |
72 | // Each predicate is encapsulated in a template class and exposes |
73 | // the static member function 'apply'. |
74 | // |
75 | //-------------------------------------------------------------------------- |
76 | |
77 | |
78 | // Because a particular logic can combine either 1 or two predicates, the following |
79 | // tags are used to allow the predicate applier to receive 2 preds, but optimize away |
80 | // one of them if it is 'non-applicable' |
81 | struct non_applicable { typedef mpl::false_ do_apply ; } ; |
82 | struct applicable { typedef mpl::true_ do_apply ; } ; |
83 | |
84 | |
85 | //-------------------------------------------------------------------------- |
86 | // |
87 | // Range Checking Logic implementations. |
88 | // |
89 | // The following classes, collectivelly named 'Predicates', are instantiated within |
90 | // the corresponding range checkers. |
91 | // Their static member function 'apply' is called to perform the actual range checking logic. |
92 | //-------------------------------------------------------------------------- |
93 | |
94 | // s < Lowest(T) ? cNegOverflow : cInRange |
95 | // |
96 | template<class Traits> |
97 | struct LT_LoT : applicable |
98 | { |
99 | typedef typename Traits::target_type T ; |
100 | typedef typename Traits::source_type S ; |
101 | typedef typename Traits::argument_type argument_type ; |
102 | |
103 | static range_check_result apply ( argument_type s ) |
104 | { |
105 | return s < static_cast<S>(bounds<T>::lowest()) ? cNegOverflow : cInRange ; |
106 | } |
107 | } ; |
108 | |
109 | // s < 0 ? cNegOverflow : cInRange |
110 | // |
111 | template<class Traits> |
112 | struct LT_Zero : applicable |
113 | { |
114 | typedef typename Traits::source_type S ; |
115 | typedef typename Traits::argument_type argument_type ; |
116 | |
117 | static range_check_result apply ( argument_type s ) |
118 | { |
119 | return s < static_cast<S>(0) ? cNegOverflow : cInRange ; |
120 | } |
121 | } ; |
122 | |
123 | // s <= Lowest(T)-1 ? cNegOverflow : cInRange |
124 | // |
125 | template<class Traits> |
126 | struct LE_PrevLoT : applicable |
127 | { |
128 | typedef typename Traits::target_type T ; |
129 | typedef typename Traits::source_type S ; |
130 | typedef typename Traits::argument_type argument_type ; |
131 | |
132 | static range_check_result apply ( argument_type s ) |
133 | { |
134 | return s <= static_cast<S>(bounds<T>::lowest()) - static_cast<S>(1.0) |
135 | ? cNegOverflow : cInRange ; |
136 | } |
137 | } ; |
138 | |
139 | // s < Lowest(T)-0.5 ? cNegOverflow : cInRange |
140 | // |
141 | template<class Traits> |
142 | struct LT_HalfPrevLoT : applicable |
143 | { |
144 | typedef typename Traits::target_type T ; |
145 | typedef typename Traits::source_type S ; |
146 | typedef typename Traits::argument_type argument_type ; |
147 | |
148 | static range_check_result apply ( argument_type s ) |
149 | { |
150 | return s < static_cast<S>(bounds<T>::lowest()) - static_cast<S>(0.5) |
151 | ? cNegOverflow : cInRange ; |
152 | } |
153 | } ; |
154 | |
155 | // s > Highest(T) ? cPosOverflow : cInRange |
156 | // |
157 | template<class Traits> |
158 | struct GT_HiT : applicable |
159 | { |
160 | typedef typename Traits::target_type T ; |
161 | typedef typename Traits::source_type S ; |
162 | typedef typename Traits::argument_type argument_type ; |
163 | |
164 | static range_check_result apply ( argument_type s ) |
165 | { |
166 | return s > static_cast<S>(bounds<T>::highest()) |
167 | ? cPosOverflow : cInRange ; |
168 | } |
169 | } ; |
170 | |
171 | // s >= Lowest(T) + 1 ? cPosOverflow : cInRange |
172 | // |
173 | template<class Traits> |
174 | struct GE_SuccHiT : applicable |
175 | { |
176 | typedef typename Traits::target_type T ; |
177 | typedef typename Traits::source_type S ; |
178 | typedef typename Traits::argument_type argument_type ; |
179 | |
180 | static range_check_result apply ( argument_type s ) |
181 | { |
182 | return s >= static_cast<S>(bounds<T>::highest()) + static_cast<S>(1.0) |
183 | ? cPosOverflow : cInRange ; |
184 | } |
185 | } ; |
186 | |
187 | // s >= Lowest(T) + 0.5 ? cPosgOverflow : cInRange |
188 | // |
189 | template<class Traits> |
190 | struct GT_HalfSuccHiT : applicable |
191 | { |
192 | typedef typename Traits::target_type T ; |
193 | typedef typename Traits::source_type S ; |
194 | typedef typename Traits::argument_type argument_type ; |
195 | |
196 | static range_check_result apply ( argument_type s ) |
197 | { |
198 | return s >= static_cast<S>(bounds<T>::highest()) + static_cast<S>(0.5) |
199 | ? cPosOverflow : cInRange ; |
200 | } |
201 | } ; |
202 | |
203 | |
204 | //-------------------------------------------------------------------------- |
205 | // |
206 | // Predicate Combiner. |
207 | // |
208 | // This helper classes are used to possibly combine the range checking logic |
209 | // individually performed by the predicates |
210 | // |
211 | //-------------------------------------------------------------------------- |
212 | |
213 | |
214 | // Applies both predicates: first 'PredA', and if it equals 'cInRange', 'PredB' |
215 | template<class PredA, class PredB> |
216 | struct applyBoth |
217 | { |
218 | typedef typename PredA::argument_type argument_type ; |
219 | |
220 | static range_check_result apply ( argument_type s ) |
221 | { |
222 | range_check_result r = PredA::apply(s) ; |
223 | if ( r == cInRange ) |
224 | r = PredB::apply(s); |
225 | return r ; |
226 | } |
227 | } ; |
228 | |
229 | template<class PredA, class PredB> |
230 | struct combine |
231 | { |
232 | typedef applyBoth<PredA,PredB> Both ; |
233 | typedef void NNone ; // 'None' is defined as a macro in (/usr/X11R6/include/X11/X.h) |
234 | |
235 | typedef typename PredA::do_apply do_applyA ; |
236 | typedef typename PredB::do_apply do_applyB ; |
237 | |
238 | typedef typename for_both<do_applyA, do_applyB, Both, PredA, PredB, NNone>::type type ; |
239 | } ; |
240 | |
241 | |
242 | |
243 | |
244 | |
245 | |
246 | |
247 | |
248 | |
249 | |
250 | |
251 | |
252 | //-------------------------------------------------------------------------- |
253 | // Range Checker classes. |
254 | // |
255 | // The following classes are VISIBLE base classes of the user-level converter<> class. |
256 | // They supply the optimized 'out_of_range()' and 'validate_range()' static member functions |
257 | // visible in the user interface. |
258 | // |
259 | //-------------------------------------------------------------------------- |
260 | |
261 | // Dummy range checker. |
262 | template<class Traits> |
263 | struct dummy_range_checker |
264 | { |
265 | typedef typename Traits::argument_type argument_type ; |
266 | |
267 | static range_check_result out_of_range ( argument_type ) { return cInRange ; } |
268 | static void validate_range ( argument_type ) {} |
269 | } ; |
270 | |
271 | // Generic range checker. |
272 | // |
273 | // All the range checking logic for all possible combinations of source and target |
274 | // can be arranged in terms of one or two predicates, which test overflow on both neg/pos 'sides' |
275 | // of the ranges. |
276 | // |
277 | // These predicates are given here as IsNegOverflow and IsPosOverflow. |
278 | // |
279 | template<class Traits, class IsNegOverflow, class IsPosOverflow, class OverflowHandler> |
280 | struct generic_range_checker |
281 | { |
282 | typedef OverflowHandler overflow_handler ; |
283 | |
284 | typedef typename Traits::argument_type argument_type ; |
285 | |
286 | static range_check_result out_of_range ( argument_type s ) |
287 | { |
288 | typedef typename combine<IsNegOverflow,IsPosOverflow>::type Predicate ; |
289 | |
290 | return Predicate::apply(s); |
291 | } |
292 | |
293 | static void validate_range ( argument_type s ) |
294 | { OverflowHandler()( out_of_range(s) ) ; } |
295 | } ; |
296 | |
297 | |
298 | |
299 | //-------------------------------------------------------------------------- |
300 | // |
301 | // Selectors for the optimized Range Checker class. |
302 | // |
303 | //-------------------------------------------------------------------------- |
304 | |
305 | template<class Traits,class OverflowHandler> |
306 | struct GetRC_Sig2Sig_or_Unsig2Unsig |
307 | { |
308 | typedef dummy_range_checker<Traits> Dummy ; |
309 | |
310 | typedef LT_LoT<Traits> Pred1 ; |
311 | typedef GT_HiT<Traits> Pred2 ; |
312 | |
313 | typedef generic_range_checker<Traits,Pred1,Pred2,OverflowHandler> Normal ; |
314 | |
315 | typedef typename Traits::subranged subranged ; |
316 | |
317 | typedef typename mpl::if_<subranged,Normal,Dummy>::type type ; |
318 | } ; |
319 | |
320 | template<class Traits, class OverflowHandler> |
321 | struct GetRC_Sig2Unsig |
322 | { |
323 | typedef LT_Zero<Traits> Pred1 ; |
324 | typedef GT_HiT <Traits> Pred2 ; |
325 | |
326 | typedef generic_range_checker<Traits,Pred1,Pred2,OverflowHandler> ChoiceA ; |
327 | |
328 | typedef generic_range_checker<Traits,Pred1,non_applicable,OverflowHandler> ChoiceB ; |
329 | |
330 | typedef typename Traits::target_type T ; |
331 | typedef typename Traits::source_type S ; |
332 | |
333 | typedef typename subranged_Unsig2Sig<S,T>::type oposite_subranged ; |
334 | |
335 | typedef typename mpl::not_<oposite_subranged>::type positively_subranged ; |
336 | |
337 | typedef typename mpl::if_<positively_subranged,ChoiceA,ChoiceB>::type type ; |
338 | } ; |
339 | |
340 | template<class Traits, class OverflowHandler> |
341 | struct GetRC_Unsig2Sig |
342 | { |
343 | typedef GT_HiT<Traits> Pred1 ; |
344 | |
345 | typedef generic_range_checker<Traits,non_applicable,Pred1,OverflowHandler> type ; |
346 | } ; |
347 | |
348 | template<class Traits,class OverflowHandler> |
349 | struct GetRC_Int2Int |
350 | { |
351 | typedef GetRC_Sig2Sig_or_Unsig2Unsig<Traits,OverflowHandler> Sig2SigQ ; |
352 | typedef GetRC_Sig2Unsig <Traits,OverflowHandler> Sig2UnsigQ ; |
353 | typedef GetRC_Unsig2Sig <Traits,OverflowHandler> Unsig2SigQ ; |
354 | typedef Sig2SigQ Unsig2UnsigQ ; |
355 | |
356 | typedef typename Traits::sign_mixture sign_mixture ; |
357 | |
358 | typedef typename |
359 | for_sign_mixture<sign_mixture,Sig2SigQ,Sig2UnsigQ,Unsig2SigQ,Unsig2UnsigQ>::type |
360 | selector ; |
361 | |
362 | typedef typename selector::type type ; |
363 | } ; |
364 | |
365 | template<class Traits> |
366 | struct GetRC_Int2Float |
367 | { |
368 | typedef dummy_range_checker<Traits> type ; |
369 | } ; |
370 | |
371 | template<class Traits, class OverflowHandler, class Float2IntRounder> |
372 | struct GetRC_Float2Int |
373 | { |
374 | typedef LE_PrevLoT <Traits> Pred1 ; |
375 | typedef GE_SuccHiT <Traits> Pred2 ; |
376 | typedef LT_HalfPrevLoT<Traits> Pred3 ; |
377 | typedef GT_HalfSuccHiT<Traits> Pred4 ; |
378 | typedef GT_HiT <Traits> Pred5 ; |
379 | typedef LT_LoT <Traits> Pred6 ; |
380 | |
381 | typedef generic_range_checker<Traits,Pred1,Pred2,OverflowHandler> ToZero ; |
382 | typedef generic_range_checker<Traits,Pred3,Pred4,OverflowHandler> ToNearest ; |
383 | typedef generic_range_checker<Traits,Pred1,Pred5,OverflowHandler> ToInf ; |
384 | typedef generic_range_checker<Traits,Pred6,Pred2,OverflowHandler> ToNegInf ; |
385 | |
386 | typedef typename Float2IntRounder::round_style round_style ; |
387 | |
388 | typedef typename for_round_style<round_style,ToZero,ToNearest,ToInf,ToNegInf>::type type ; |
389 | } ; |
390 | |
391 | template<class Traits, class OverflowHandler> |
392 | struct GetRC_Float2Float |
393 | { |
394 | typedef dummy_range_checker<Traits> Dummy ; |
395 | |
396 | typedef LT_LoT<Traits> Pred1 ; |
397 | typedef GT_HiT<Traits> Pred2 ; |
398 | |
399 | typedef generic_range_checker<Traits,Pred1,Pred2,OverflowHandler> Normal ; |
400 | |
401 | typedef typename Traits::subranged subranged ; |
402 | |
403 | typedef typename mpl::if_<subranged,Normal,Dummy>::type type ; |
404 | } ; |
405 | |
406 | template<class Traits, class OverflowHandler, class Float2IntRounder> |
407 | struct GetRC_BuiltIn2BuiltIn |
408 | { |
409 | typedef GetRC_Int2Int<Traits,OverflowHandler> Int2IntQ ; |
410 | typedef GetRC_Int2Float<Traits> Int2FloatQ ; |
411 | typedef GetRC_Float2Int<Traits,OverflowHandler,Float2IntRounder> Float2IntQ ; |
412 | typedef GetRC_Float2Float<Traits,OverflowHandler> Float2FloatQ ; |
413 | |
414 | typedef typename Traits::int_float_mixture int_float_mixture ; |
415 | |
416 | typedef typename for_int_float_mixture<int_float_mixture, Int2IntQ, Int2FloatQ, Float2IntQ, Float2FloatQ>::type selector ; |
417 | |
418 | typedef typename selector::type type ; |
419 | } ; |
420 | |
421 | template<class Traits, class OverflowHandler, class Float2IntRounder> |
422 | struct GetRC |
423 | { |
424 | typedef GetRC_BuiltIn2BuiltIn<Traits,OverflowHandler,Float2IntRounder> BuiltIn2BuiltInQ ; |
425 | |
426 | typedef dummy_range_checker<Traits> Dummy ; |
427 | |
428 | typedef mpl::identity<Dummy> DummyQ ; |
429 | |
430 | typedef typename Traits::udt_builtin_mixture udt_builtin_mixture ; |
431 | |
432 | typedef typename for_udt_builtin_mixture<udt_builtin_mixture,BuiltIn2BuiltInQ,DummyQ,DummyQ,DummyQ>::type selector ; |
433 | |
434 | typedef typename selector::type type ; |
435 | } ; |
436 | |
437 | |
438 | |
439 | |
440 | //-------------------------------------------------------------------------- |
441 | // Converter classes. |
442 | // |
443 | // The following classes are VISIBLE base classes of the user-level converter<> class. |
444 | // They supply the optimized 'nearbyint()' and 'convert()' static member functions |
445 | // visible in the user interface. |
446 | // |
447 | //-------------------------------------------------------------------------- |
448 | |
449 | // |
450 | // Trivial Converter : used when (cv-unqualified) T == (cv-unqualified) S |
451 | // |
452 | template<class Traits> |
453 | struct trivial_converter_impl : public std::unary_function< BOOST_DEDUCED_TYPENAME Traits::argument_type |
454 | ,BOOST_DEDUCED_TYPENAME Traits::result_type |
455 | > |
456 | ,public dummy_range_checker<Traits> |
457 | { |
458 | typedef Traits traits ; |
459 | |
460 | typedef typename Traits::source_type source_type ; |
461 | typedef typename Traits::argument_type argument_type ; |
462 | typedef typename Traits::result_type result_type ; |
463 | |
464 | static result_type low_level_convert ( argument_type s ) { return s ; } |
465 | static source_type nearbyint ( argument_type s ) { return s ; } |
466 | static result_type convert ( argument_type s ) { return s ; } |
467 | } ; |
468 | |
469 | |
470 | // |
471 | // Rounding Converter : used for float to integral conversions. |
472 | // |
473 | template<class Traits,class RangeChecker,class RawConverter,class Float2IntRounder> |
474 | struct rounding_converter : public std::unary_function< BOOST_DEDUCED_TYPENAME Traits::argument_type |
475 | ,BOOST_DEDUCED_TYPENAME Traits::result_type |
476 | > |
477 | ,public RangeChecker |
478 | ,public Float2IntRounder |
479 | ,public RawConverter |
480 | { |
481 | typedef RangeChecker RangeCheckerBase ; |
482 | typedef Float2IntRounder Float2IntRounderBase ; |
483 | typedef RawConverter RawConverterBase ; |
484 | |
485 | typedef Traits traits ; |
486 | |
487 | typedef typename Traits::source_type source_type ; |
488 | typedef typename Traits::argument_type argument_type ; |
489 | typedef typename Traits::result_type result_type ; |
490 | |
491 | static result_type convert ( argument_type s ) |
492 | { |
493 | RangeCheckerBase::validate_range(s); |
494 | source_type s1 = Float2IntRounderBase::nearbyint(s); |
495 | return RawConverterBase::low_level_convert(s1); |
496 | } |
497 | } ; |
498 | |
499 | |
500 | // |
501 | // Non-Rounding Converter : used for all other conversions. |
502 | // |
503 | template<class Traits,class RangeChecker,class RawConverter> |
504 | struct non_rounding_converter : public std::unary_function< BOOST_DEDUCED_TYPENAME Traits::argument_type |
505 | ,BOOST_DEDUCED_TYPENAME Traits::result_type |
506 | > |
507 | ,public RangeChecker |
508 | ,public RawConverter |
509 | { |
510 | typedef RangeChecker RangeCheckerBase ; |
511 | typedef RawConverter RawConverterBase ; |
512 | |
513 | typedef Traits traits ; |
514 | |
515 | typedef typename Traits::source_type source_type ; |
516 | typedef typename Traits::argument_type argument_type ; |
517 | typedef typename Traits::result_type result_type ; |
518 | |
519 | static source_type nearbyint ( argument_type s ) { return s ; } |
520 | |
521 | static result_type convert ( argument_type s ) |
522 | { |
523 | RangeCheckerBase::validate_range(s); |
524 | return RawConverterBase::low_level_convert(s); |
525 | } |
526 | } ; |
527 | |
528 | |
529 | |
530 | |
531 | //-------------------------------------------------------------------------- |
532 | // |
533 | // Selectors for the optimized Converter class. |
534 | // |
535 | //-------------------------------------------------------------------------- |
536 | |
537 | template<class Traits,class OverflowHandler,class Float2IntRounder,class RawConverter, class UserRangeChecker> |
538 | struct get_non_trivial_converter |
539 | { |
540 | typedef GetRC<Traits,OverflowHandler,Float2IntRounder> InternalRangeCheckerQ ; |
541 | |
542 | typedef is_same<UserRangeChecker,UseInternalRangeChecker> use_internal_RC ; |
543 | |
544 | typedef mpl::identity<UserRangeChecker> UserRangeCheckerQ ; |
545 | |
546 | typedef typename |
547 | mpl::eval_if<use_internal_RC,InternalRangeCheckerQ,UserRangeCheckerQ>::type |
548 | RangeChecker ; |
549 | |
550 | typedef non_rounding_converter<Traits,RangeChecker,RawConverter> NonRounding ; |
551 | typedef rounding_converter<Traits,RangeChecker,RawConverter,Float2IntRounder> Rounding ; |
552 | |
553 | typedef mpl::identity<NonRounding> NonRoundingQ ; |
554 | typedef mpl::identity<Rounding> RoundingQ ; |
555 | |
556 | typedef typename Traits::int_float_mixture int_float_mixture ; |
557 | |
558 | typedef typename |
559 | for_int_float_mixture<int_float_mixture, NonRoundingQ, NonRoundingQ, RoundingQ, NonRoundingQ>::type |
560 | selector ; |
561 | |
562 | typedef typename selector::type type ; |
563 | } ; |
564 | |
565 | template< class Traits |
566 | ,class OverflowHandler |
567 | ,class Float2IntRounder |
568 | ,class RawConverter |
569 | ,class UserRangeChecker |
570 | > |
571 | struct get_converter_impl |
572 | { |
573 | #if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT( 0x0561 ) ) |
574 | // bcc55 prefers sometimes template parameters to be explicit local types. |
575 | // (notice that is is illegal to reuse the names like this) |
576 | typedef Traits Traits ; |
577 | typedef OverflowHandler OverflowHandler ; |
578 | typedef Float2IntRounder Float2IntRounder ; |
579 | typedef RawConverter RawConverter ; |
580 | typedef UserRangeChecker UserRangeChecker ; |
581 | #endif |
582 | |
583 | typedef trivial_converter_impl<Traits> Trivial ; |
584 | typedef mpl::identity <Trivial> TrivialQ ; |
585 | |
586 | typedef get_non_trivial_converter< Traits |
587 | ,OverflowHandler |
588 | ,Float2IntRounder |
589 | ,RawConverter |
590 | ,UserRangeChecker |
591 | > NonTrivialQ ; |
592 | |
593 | typedef typename Traits::trivial trivial ; |
594 | |
595 | typedef typename mpl::eval_if<trivial,TrivialQ,NonTrivialQ>::type type ; |
596 | } ; |
597 | |
598 | } } } // namespace boost::numeric::convdetail |
599 | |
600 | #endif |
601 | |
602 | |
603 | |