]> git.stg.codes - stg.git/blob - tests/tut/tut_fpt.hpp
Merge branch 'master' into full-month-stats
[stg.git] / tests / tut / tut_fpt.hpp
1 /**
2  * @brief  Additional ensures for scientific/engineering applications.
3  * @author Joerg <yogi2005@users.sourceforge.net>
4  * @date   07/04/2008
5  */
6 #ifndef TUT_Float_H_GUARD
7 #define TUT_Float_H_GUARD
8
9 #include <limits>
10 #include <iostream>
11
12 namespace tut
13 {
14     namespace detail
15     {
16         template<bool Predicate, typename Then, typename Else>
17         struct If
18         {
19             typedef Else type;
20         };
21
22         template<typename Then, typename Else>
23         struct If<true,Then,Else>
24         {
25             typedef Then type;
26         };
27
28         template<typename T>
29         struct fpt_traits
30         {
31             struct StdNumericLimitsNotAvailable {};
32             static const StdNumericLimitsNotAvailable static_check[ std::numeric_limits<T>::is_specialized ];
33
34             static const T zero;
35
36             typedef typename If<std::numeric_limits<T>::is_integer,
37                                 double,
38                                 T>::type Result;
39
40             static T abs(const T &arg)
41             {
42                 if(arg < zero)
43                     return zero - arg;
44                 else
45                     return arg;
46             }
47
48             static T sig(const T &arg)
49             {
50                 if(arg < zero)
51                     return -1;
52                 else
53                     return 1;
54             }
55
56             static inline Result div(const Result &number, const T &divisor)
57             {
58                 static_cast<void>(static_check);
59
60                 if(number == zero && divisor == zero)
61                     return std::numeric_limits<Result>::quiet_NaN();
62
63                 if(number == zero)
64                     return zero;
65
66                 if(divisor == zero)
67                     return sig(number) * std::numeric_limits<Result>::infinity();
68
69                 assert(zero < number);
70                 assert(zero < divisor);
71
72                 // Avoid underflow
73                 if(static_cast<T>(1) < abs(divisor))
74                 {
75                     // number / divisor < min <=> number < min * divisor
76                     if( abs(number) < abs(divisor) * std::numeric_limits<T>::min())
77                     {
78                         return sig(divisor) * sig(number) * std::numeric_limits<T>::min();
79                     }
80                 }
81
82                 // Avoid overflow
83                 if( abs(divisor) < static_cast<T>(1))
84                 {
85                     // number / divisor > max <=> number > max * divisor
86                     if( abs(divisor) * std::numeric_limits<T>::max() < abs(number))
87                     {
88                         return sig(divisor) * sig(number) * std::numeric_limits<T>::max();
89                     }
90                 }
91
92                 return number / divisor;
93             }
94         };
95
96         template<typename T>
97         const typename fpt_traits<T>::StdNumericLimitsNotAvailable
98             fpt_traits<T>::static_check[ std::numeric_limits<T>::is_specialized ] = { {} };
99
100         template<typename T>
101         const T fpt_traits<T>::zero = static_cast<T>(0);
102
103         template<typename T, typename U>
104         bool check_tolerance(T actual, T expected, U fraction)
105         {
106             typename fpt_traits<T>::Result diff = fpt_traits<T>::div( fpt_traits<T>::abs( expected - actual ),
107                                                                       fpt_traits<T>::abs( expected ) );
108
109             return (diff == fraction) || (diff < fraction);
110         }
111
112     } // namespace detail
113
114     template<typename T, typename U>
115     void ensure_close(const char* msg, const T& actual, const T& expected, const U& tolerance )
116     {
117         typedef detail::fpt_traits<U> Traits;
118
119         typename Traits::Result fraction = Traits::div( Traits::abs(static_cast<typename Traits::Result>(tolerance)),
120                                                         static_cast<typename Traits::Result>(100) );
121         if( !detail::check_tolerance(actual, expected, fraction) )
122         {
123             std::ostringstream ss;
124             ss << ( msg ? msg : "" )
125             << ( msg ? ": " : "" )
126             << "expected `"
127             << expected
128             << "` and actual `"
129             << actual
130             << "` differ more than "
131             << tolerance
132             << "%";
133              throw failure( ss.str().c_str() );
134         }
135     }
136
137     template<typename T, typename Tolerance>
138     void ensure_close(const T& actual, const T& expected, const Tolerance& tolerance )
139     {
140         ensure_close( 0, actual, expected, tolerance );
141     }
142
143     template<typename T, typename U>
144     void ensure_close_fraction(const char* msg, const T& actual, const T& expected, const U& fraction)
145     {
146         typedef char StdNumericLimitsNotAvailable;
147         const StdNumericLimitsNotAvailable static_check[ std::numeric_limits<U>::is_specialized ] = { 0 };
148         static_cast<void>(static_check);
149
150         typedef typename detail::If<std::numeric_limits<U>::is_integer,
151                                     double,
152                                     U>::type Tolerance;
153
154         if( !detail::check_tolerance(actual, expected, fraction) )
155         {
156             std::ostringstream ss;
157             ss << ( msg ? msg : "" )
158             << ( msg ? ": " : "" )
159             << "expected `"
160             << expected
161             << "` and actual `"
162             << actual
163             << "` differ more than fraction `"
164             << fraction
165             << "`";
166             throw failure( ss.str().c_str() );
167         }
168     }
169
170     template<typename T>
171     void ensure_close_fraction( const char* msg, const T& actual, const T& expected, const int& tolerance )
172     {
173         ensure_close(msg, actual, expected, double(tolerance));
174     }
175
176     template< typename T, typename Tolerance>
177     void ensure_close_fraction(const T& actual, const T& expected, const Tolerance& fraction)
178     {
179         ensure_close_fraction( 0, actual, expected, fraction );
180     }
181
182 } // namespace tut
183
184 #endif
185