forked from boostorg/math
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpolicy_eg_9.cpp
More file actions
313 lines (254 loc) · 9.5 KB
/
Copy pathpolicy_eg_9.cpp
File metadata and controls
313 lines (254 loc) · 9.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
// Copyright John Maddock 2007.
// Copyright Paul A. Bristow 2010
// Use, modification and distribution are subject to the
// Boost Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// Note that this file contains quickbook mark-up as well as code
// and comments, don't change any of the special comment mark-ups!
#include <iostream>
#include <boost/format.hpp>
using std::cout; using std::endl; using std::cerr;
//[policy_eg_9
/*`
The previous example was all well and good, but the custom error handlers
didn't really do much of any use. In this example we'll implement all
the custom handlers and show how the information provided to them can be
used to generate nice formatted error messages.
Each error handler has the general form:
template <class T>
T user_``['error_type]``(
const char* function,
const char* message,
const T& val);
and accepts three arguments:
[variablelist
[[const char* function]
[The name of the function that raised the error, this string
contains one or more %1% format specifiers that should be
replaced by the name of real type T, like float or double.]]
[[const char* message]
[A message associated with the error, normally this
contains a %1% format specifier that should be replaced with
the value of ['value]: however note that overflow and underflow messages
do not contain this %1% specifier (since the value of ['value] is
immaterial in these cases).]]
[[const T& value]
[The value that caused the error: either an argument to the function
if this is a domain or pole error, the tentative result
if this is a denorm or evaluation error, or zero or infinity for
underflow or overflow errors.]]
]
As before we'll include the headers we need first:
*/
#include <boost/math/special_functions.hpp>
/*`
Next we'll implement our own error handlers for each type of error,
starting with domain errors:
*/
namespace boost{ namespace math{
namespace policies
{
template <class T>
T user_domain_error(const char* function, const char* message, const T& val)
{
/*`
We'll begin with a bit of defensive programming in case function or message are empty:
*/
if(function == 0)
function = "Unknown function with arguments of type %1%";
if(message == 0)
message = "Cause unknown with bad argument %1%";
/*`
Next we'll format the name of the function with the name of type T, perhaps double:
*/
std::string msg("Error in function ");
msg += (boost::format(function) % typeid(T).name()).str();
/*`
Then likewise format the error message with the value of parameter /val/,
making sure we output all the potentially significant digits of /val/:
*/
msg += ": \n";
int prec = 2 + (std::numeric_limits<T>::digits * 30103UL) / 100000UL;
// int prec = std::numeric_limits<T>::max_digits10; // For C++0X Standard Library
msg += (boost::format(message) % boost::io::group(std::setprecision(prec), val)).str();
/*`
Now we just have to do something with the message, we could throw an
exception, but for the purposes of this example we'll just dump the message
to std::cerr:
*/
std::cerr << msg << std::endl;
/*`
Finally the only sensible value we can return from a domain error is a NaN:
*/
return std::numeric_limits<T>::quiet_NaN();
}
/*`
Pole errors are essentially a special case of domain errors,
so in this example we'll just return the result of a domain error:
*/
template <class T>
T user_pole_error(const char* function, const char* message, const T& val)
{
return user_domain_error(function, message, val);
}
/*`
Overflow errors are very similar to domain errors, except that there's
no %1% format specifier in the /message/ parameter:
*/
template <class T>
T user_overflow_error(const char* function, const char* message, const T& val)
{
if(function == 0)
function = "Unknown function with arguments of type %1%";
if(message == 0)
message = "Result of function is too large to represent";
std::string msg("Error in function ");
msg += (boost::format(function) % typeid(T).name()).str();
msg += ": \n";
msg += message;
std::cerr << msg << std::endl;
// Value passed to the function is an infinity, just return it:
return val;
}
/*`
Underflow errors are much the same as overflow:
*/
template <class T>
T user_underflow_error(const char* function, const char* message, const T& val)
{
if(function == 0)
function = "Unknown function with arguments of type %1%";
if(message == 0)
message = "Result of function is too small to represent";
std::string msg("Error in function ");
msg += (boost::format(function) % typeid(T).name()).str();
msg += ": \n";
msg += message;
std::cerr << msg << std::endl;
// Value passed to the function is zero, just return it:
return val;
}
/*`
Denormalised results are much the same as underflow:
*/
template <class T>
T user_denorm_error(const char* function, const char* message, const T& val)
{
if(function == 0)
function = "Unknown function with arguments of type %1%";
if(message == 0)
message = "Result of function is denormalised";
std::string msg("Error in function ");
msg += (boost::format(function) % typeid(T).name()).str();
msg += ": \n";
msg += message;
std::cerr << msg << std::endl;
// Value passed to the function is denormalised, just return it:
return val;
}
/*`
Which leaves us with evaluation errors: these occur when an internal
error occurs that prevents the function being fully evaluated.
The parameter /val/ contains the closest approximation to the result
found so far:
*/
template <class T>
T user_evaluation_error(const char* function, const char* message, const T& val)
{
if(function == 0)
function = "Unknown function with arguments of type %1%";
if(message == 0)
message = "An internal evaluation error occurred with "
"the best value calculated so far of %1%";
std::string msg("Error in function ");
msg += (boost::format(function) % typeid(T).name()).str();
msg += ": \n";
int prec = 2 + (std::numeric_limits<T>::digits * 30103UL) / 100000UL;
// int prec = std::numeric_limits<T>::max_digits10; // For C++0X Standard Library
msg += (boost::format(message) % boost::io::group(std::setprecision(prec), val)).str();
std::cerr << msg << std::endl;
// What do we return here? This is generally a fatal error, that should never occur,
// so we just return a NaN for the purposes of the example:
return std::numeric_limits<T>::quiet_NaN();
}
} // policies
}} // boost::math
/*`
Now we'll need to define a suitable policy that will call these handlers,
and define some forwarding functions that make use of the policy:
*/
namespace mymath
{ // unnamed.
using namespace boost::math::policies;
typedef policy<
domain_error<user_error>,
pole_error<user_error>,
overflow_error<user_error>,
underflow_error<user_error>,
denorm_error<user_error>,
evaluation_error<user_error>
> user_error_policy;
BOOST_MATH_DECLARE_SPECIAL_FUNCTIONS(user_error_policy)
} // unnamed namespace
/*`
We now have a set of forwarding functions, defined in namespace mymath,
that all look something like this:
``
template <class RealType>
inline typename boost::math::tools::promote_args<RT>::type
tgamma(RT z)
{
return boost::math::tgamma(z, user_error_policy());
}
``
So that when we call `mymath::tgamma(z)` we really end up calling
`boost::math::tgamma(z, user_error_policy())`, and any
errors will get directed to our own error handlers:
*/
int main()
{
// Raise a domain error:
cout << "Result of erf_inv(-10) is: "
<< mymath::erf_inv(-10) << std::endl << endl;
// Raise a pole error:
cout << "Result of tgamma(-10) is: "
<< mymath::tgamma(-10) << std::endl << endl;
// Raise an overflow error:
cout << "Result of tgamma(3000) is: "
<< mymath::tgamma(3000) << std::endl << endl;
// Raise an underflow error:
cout << "Result of tgamma(-190.5) is: "
<< mymath::tgamma(-190.5) << std::endl << endl;
// Unfortunately we can't predicably raise a denormalised
// result, nor can we raise an evaluation error in this example
// since these should never really occur!
} // int main()
/*`
Which outputs:
[pre
Error in function boost::math::erf_inv<double>(double, double):
Argument outside range \[-1, 1\] in inverse erf function (got p=-10).
Result of erf_inv(-10) is: 1.#QNAN
Error in function boost::math::tgamma<long double>(long double):
Evaluation of tgamma at a negative integer -10.
Result of tgamma(-10) is: 1.#QNAN
Error in function boost::math::tgamma<long double>(long double):
Result of tgamma is too large to represent.
Error in function boost::math::tgamma<double>(double):
Result of function is too large to represent
Result of tgamma(3000) is: 1.#INF
Error in function boost::math::tgamma<long double>(long double):
Result of tgamma is too large to represent.
Error in function boost::math::tgamma<long double>(long double):
Result of tgamma is too small to represent.
Result of tgamma(-190.5) is: 0
]
Notice how some of the calls result in an error handler being called more
than once, or for more than one handler to be called: this is an artefact
of the fact that many functions are implemented in terms of one or more
sub-routines each of which may have it's own error handling. For example
`tgamma(-190.5)` is implemented in terms of `tgamma(190.5)` - which overflows -
the reflection formula for `tgamma` then notices that it is dividing by
infinity and so underflows.
*/
//] //[/policy_eg_9]