// Copyright John Maddock 2007. | |
// Copyright Paul A. Bristow 2007. | |
// 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) | |
#ifndef BOOST_MATH_POLICY_ERROR_HANDLING_HPP | |
#define BOOST_MATH_POLICY_ERROR_HANDLING_HPP | |
#include <stdexcept> | |
#include <iomanip> | |
#include <string> | |
#include <cerrno> | |
#include <boost/config/no_tr1/cmath.hpp> | |
#include <stdexcept> | |
#include <boost/math/tools/config.hpp> | |
#include <boost/math/policies/policy.hpp> | |
#include <boost/math/tools/precision.hpp> | |
#include <boost/cstdint.hpp> | |
#ifdef BOOST_MSVC | |
# pragma warning(push) // Quiet warnings in boost/format.hpp | |
# pragma warning(disable: 4996) // _SCL_SECURE_NO_DEPRECATE | |
# pragma warning(disable: 4512) // assignment operator could not be generated. | |
// And warnings in error handling: | |
# pragma warning(disable: 4702) // unreachable code | |
// Note that this only occurs when the compiler can deduce code is unreachable, | |
// for example when policy macros are used to ignore errors rather than throw. | |
#endif | |
#include <boost/format.hpp> | |
namespace boost{ namespace math{ | |
class evaluation_error : public std::runtime_error | |
{ | |
public: | |
evaluation_error(const std::string& s) : std::runtime_error(s){} | |
}; | |
class rounding_error : public std::runtime_error | |
{ | |
public: | |
rounding_error(const std::string& s) : std::runtime_error(s){} | |
}; | |
namespace policies{ | |
// | |
// Forward declarations of user error handlers, | |
// it's up to the user to provide the definition of these: | |
// | |
template <class T> | |
T user_domain_error(const char* function, const char* message, const T& val); | |
template <class T> | |
T user_pole_error(const char* function, const char* message, const T& val); | |
template <class T> | |
T user_overflow_error(const char* function, const char* message, const T& val); | |
template <class T> | |
T user_underflow_error(const char* function, const char* message, const T& val); | |
template <class T> | |
T user_denorm_error(const char* function, const char* message, const T& val); | |
template <class T> | |
T user_evaluation_error(const char* function, const char* message, const T& val); | |
template <class T, class TargetType> | |
T user_rounding_error(const char* function, const char* message, const T& val, const TargetType& t); | |
template <class T> | |
T user_indeterminate_result_error(const char* function, const char* message, const T& val); | |
namespace detail | |
{ | |
// | |
// Helper function to avoid binding rvalue to non-const-reference, | |
// in other words a warning suppression mechansim: | |
// | |
template <class Formatter, class Group> | |
inline std::string do_format(Formatter f, const Group& g) | |
{ | |
return (f % g).str(); | |
} | |
template <class E, class T> | |
void raise_error(const char* function, const char* message) | |
{ | |
if(function == 0) | |
function = "Unknown function operating on type %1%"; | |
if(message == 0) | |
message = "Cause unknown"; | |
std::string msg("Error in function "); | |
msg += (boost::format(function) % typeid(T).name()).str(); | |
msg += ": "; | |
msg += message; | |
E e(msg); | |
boost::throw_exception(e); | |
} | |
template <class E, class T> | |
void raise_error(const char* function, const char* message, const T& val) | |
{ | |
if(function == 0) | |
function = "Unknown function operating on type %1%"; | |
if(message == 0) | |
message = "Cause unknown: error caused by bad argument with value %1%"; | |
std::string msg("Error in function "); | |
msg += (boost::format(function) % typeid(T).name()).str(); | |
msg += ": "; | |
msg += message; | |
int prec = 2 + (boost::math::policies::digits<T, boost::math::policies::policy<> >() * 30103UL) / 100000UL; | |
msg = do_format(boost::format(msg), boost::io::group(std::setprecision(prec), val)); | |
E e(msg); | |
boost::throw_exception(e); | |
} | |
template <class T> | |
inline T raise_domain_error( | |
const char* function, | |
const char* message, | |
const T& val, | |
const ::boost::math::policies::domain_error< ::boost::math::policies::throw_on_error>&) | |
{ | |
raise_error<std::domain_error, T>(function, message, val); | |
// we never get here: | |
return std::numeric_limits<T>::quiet_NaN(); | |
} | |
template <class T> | |
inline T raise_domain_error( | |
const char* , | |
const char* , | |
const T& , | |
const ::boost::math::policies::domain_error< ::boost::math::policies::ignore_error>&) | |
{ | |
// This may or may not do the right thing, but the user asked for the error | |
// to be ignored so here we go anyway: | |
return std::numeric_limits<T>::quiet_NaN(); | |
} | |
template <class T> | |
inline T raise_domain_error( | |
const char* , | |
const char* , | |
const T& , | |
const ::boost::math::policies::domain_error< ::boost::math::policies::errno_on_error>&) | |
{ | |
errno = EDOM; | |
// This may or may not do the right thing, but the user asked for the error | |
// to be silent so here we go anyway: | |
return std::numeric_limits<T>::quiet_NaN(); | |
} | |
template <class T> | |
inline T raise_domain_error( | |
const char* function, | |
const char* message, | |
const T& val, | |
const ::boost::math::policies::domain_error< ::boost::math::policies::user_error>&) | |
{ | |
return user_domain_error(function, message, val); | |
} | |
template <class T> | |
inline T raise_pole_error( | |
const char* function, | |
const char* message, | |
const T& val, | |
const ::boost::math::policies::pole_error< ::boost::math::policies::throw_on_error>&) | |
{ | |
return boost::math::policies::detail::raise_domain_error(function, message, val, ::boost::math::policies::domain_error< ::boost::math::policies::throw_on_error>()); | |
} | |
template <class T> | |
inline T raise_pole_error( | |
const char* function, | |
const char* message, | |
const T& val, | |
const ::boost::math::policies::pole_error< ::boost::math::policies::ignore_error>&) | |
{ | |
return ::boost::math::policies::detail::raise_domain_error(function, message, val, ::boost::math::policies::domain_error< ::boost::math::policies::ignore_error>()); | |
} | |
template <class T> | |
inline T raise_pole_error( | |
const char* function, | |
const char* message, | |
const T& val, | |
const ::boost::math::policies::pole_error< ::boost::math::policies::errno_on_error>&) | |
{ | |
return ::boost::math::policies::detail::raise_domain_error(function, message, val, ::boost::math::policies::domain_error< ::boost::math::policies::errno_on_error>()); | |
} | |
template <class T> | |
inline T raise_pole_error( | |
const char* function, | |
const char* message, | |
const T& val, | |
const ::boost::math::policies::pole_error< ::boost::math::policies::user_error>&) | |
{ | |
return user_pole_error(function, message, val); | |
} | |
template <class T> | |
inline T raise_overflow_error( | |
const char* function, | |
const char* message, | |
const ::boost::math::policies::overflow_error< ::boost::math::policies::throw_on_error>&) | |
{ | |
raise_error<std::overflow_error, T>(function, message ? message : "numeric overflow"); | |
// we never get here: | |
return std::numeric_limits<T>::has_infinity ? std::numeric_limits<T>::infinity() : boost::math::tools::max_value<T>(); | |
} | |
template <class T> | |
inline T raise_overflow_error( | |
const char* , | |
const char* , | |
const ::boost::math::policies::overflow_error< ::boost::math::policies::ignore_error>&) | |
{ | |
// This may or may not do the right thing, but the user asked for the error | |
// to be ignored so here we go anyway: | |
return std::numeric_limits<T>::has_infinity ? std::numeric_limits<T>::infinity() : boost::math::tools::max_value<T>(); | |
} | |
template <class T> | |
inline T raise_overflow_error( | |
const char* , | |
const char* , | |
const ::boost::math::policies::overflow_error< ::boost::math::policies::errno_on_error>&) | |
{ | |
errno = ERANGE; | |
// This may or may not do the right thing, but the user asked for the error | |
// to be silent so here we go anyway: | |
return std::numeric_limits<T>::has_infinity ? std::numeric_limits<T>::infinity() : boost::math::tools::max_value<T>(); | |
} | |
template <class T> | |
inline T raise_overflow_error( | |
const char* function, | |
const char* message, | |
const ::boost::math::policies::overflow_error< ::boost::math::policies::user_error>&) | |
{ | |
return user_overflow_error(function, message, std::numeric_limits<T>::infinity()); | |
} | |
template <class T> | |
inline T raise_underflow_error( | |
const char* function, | |
const char* message, | |
const ::boost::math::policies::underflow_error< ::boost::math::policies::throw_on_error>&) | |
{ | |
raise_error<std::underflow_error, T>(function, message ? message : "numeric underflow"); | |
// we never get here: | |
return 0; | |
} | |
template <class T> | |
inline T raise_underflow_error( | |
const char* , | |
const char* , | |
const ::boost::math::policies::underflow_error< ::boost::math::policies::ignore_error>&) | |
{ | |
// This may or may not do the right thing, but the user asked for the error | |
// to be ignored so here we go anyway: | |
return T(0); | |
} | |
template <class T> | |
inline T raise_underflow_error( | |
const char* /* function */, | |
const char* /* message */, | |
const ::boost::math::policies::underflow_error< ::boost::math::policies::errno_on_error>&) | |
{ | |
errno = ERANGE; | |
// This may or may not do the right thing, but the user asked for the error | |
// to be silent so here we go anyway: | |
return T(0); | |
} | |
template <class T> | |
inline T raise_underflow_error( | |
const char* function, | |
const char* message, | |
const ::boost::math::policies::underflow_error< ::boost::math::policies::user_error>&) | |
{ | |
return user_underflow_error(function, message, T(0)); | |
} | |
template <class T> | |
inline T raise_denorm_error( | |
const char* function, | |
const char* message, | |
const T& /* val */, | |
const ::boost::math::policies::denorm_error< ::boost::math::policies::throw_on_error>&) | |
{ | |
raise_error<std::underflow_error, T>(function, message ? message : "denormalised result"); | |
// we never get here: | |
return T(0); | |
} | |
template <class T> | |
inline T raise_denorm_error( | |
const char* , | |
const char* , | |
const T& val, | |
const ::boost::math::policies::denorm_error< ::boost::math::policies::ignore_error>&) | |
{ | |
// This may or may not do the right thing, but the user asked for the error | |
// to be ignored so here we go anyway: | |
return val; | |
} | |
template <class T> | |
inline T raise_denorm_error( | |
const char* , | |
const char* , | |
const T& val, | |
const ::boost::math::policies::denorm_error< ::boost::math::policies::errno_on_error>&) | |
{ | |
errno = ERANGE; | |
// This may or may not do the right thing, but the user asked for the error | |
// to be silent so here we go anyway: | |
return val; | |
} | |
template <class T> | |
inline T raise_denorm_error( | |
const char* function, | |
const char* message, | |
const T& val, | |
const ::boost::math::policies::denorm_error< ::boost::math::policies::user_error>&) | |
{ | |
return user_denorm_error(function, message, val); | |
} | |
template <class T> | |
inline T raise_evaluation_error( | |
const char* function, | |
const char* message, | |
const T& val, | |
const ::boost::math::policies::evaluation_error< ::boost::math::policies::throw_on_error>&) | |
{ | |
raise_error<boost::math::evaluation_error, T>(function, message, val); | |
// we never get here: | |
return T(0); | |
} | |
template <class T> | |
inline T raise_evaluation_error( | |
const char* , | |
const char* , | |
const T& val, | |
const ::boost::math::policies::evaluation_error< ::boost::math::policies::ignore_error>&) | |
{ | |
// This may or may not do the right thing, but the user asked for the error | |
// to be ignored so here we go anyway: | |
return val; | |
} | |
template <class T> | |
inline T raise_evaluation_error( | |
const char* , | |
const char* , | |
const T& val, | |
const ::boost::math::policies::evaluation_error< ::boost::math::policies::errno_on_error>&) | |
{ | |
errno = EDOM; | |
// This may or may not do the right thing, but the user asked for the error | |
// to be silent so here we go anyway: | |
return val; | |
} | |
template <class T> | |
inline T raise_evaluation_error( | |
const char* function, | |
const char* message, | |
const T& val, | |
const ::boost::math::policies::evaluation_error< ::boost::math::policies::user_error>&) | |
{ | |
return user_evaluation_error(function, message, val); | |
} | |
template <class T, class TargetType> | |
inline T raise_rounding_error( | |
const char* function, | |
const char* message, | |
const T& val, | |
const TargetType&, | |
const ::boost::math::policies::rounding_error< ::boost::math::policies::throw_on_error>&) | |
{ | |
raise_error<boost::math::rounding_error, T>(function, message, val); | |
// we never get here: | |
return T(0); | |
} | |
template <class T, class TargetType> | |
inline T raise_rounding_error( | |
const char* , | |
const char* , | |
const T& val, | |
const TargetType&, | |
const ::boost::math::policies::rounding_error< ::boost::math::policies::ignore_error>&) | |
{ | |
// This may or may not do the right thing, but the user asked for the error | |
// to be ignored so here we go anyway: | |
return std::numeric_limits<T>::is_specialized ? (val > 0 ? (std::numeric_limits<T>::max)() : -(std::numeric_limits<T>::max)()): val; | |
} | |
template <class T, class TargetType> | |
inline T raise_rounding_error( | |
const char* , | |
const char* , | |
const T& val, | |
const TargetType&, | |
const ::boost::math::policies::rounding_error< ::boost::math::policies::errno_on_error>&) | |
{ | |
errno = ERANGE; | |
// This may or may not do the right thing, but the user asked for the error | |
// to be silent so here we go anyway: | |
return std::numeric_limits<T>::is_specialized ? (val > 0 ? (std::numeric_limits<T>::max)() : -(std::numeric_limits<T>::max)()): val; | |
} | |
template <class T, class TargetType> | |
inline T raise_rounding_error( | |
const char* function, | |
const char* message, | |
const T& val, | |
const TargetType& t, | |
const ::boost::math::policies::rounding_error< ::boost::math::policies::user_error>&) | |
{ | |
return user_rounding_error(function, message, val, t); | |
} | |
template <class T, class R> | |
inline T raise_indeterminate_result_error( | |
const char* function, | |
const char* message, | |
const T& val, | |
const R& , | |
const ::boost::math::policies::indeterminate_result_error< ::boost::math::policies::throw_on_error>&) | |
{ | |
raise_error<std::domain_error, T>(function, message, val); | |
// we never get here: | |
return std::numeric_limits<T>::quiet_NaN(); | |
} | |
template <class T, class R> | |
inline T raise_indeterminate_result_error( | |
const char* , | |
const char* , | |
const T& , | |
const R& result, | |
const ::boost::math::policies::indeterminate_result_error< ::boost::math::policies::ignore_error>&) | |
{ | |
// This may or may not do the right thing, but the user asked for the error | |
// to be ignored so here we go anyway: | |
return result; | |
} | |
template <class T, class R> | |
inline T raise_indeterminate_result_error( | |
const char* , | |
const char* , | |
const T& , | |
const R& result, | |
const ::boost::math::policies::indeterminate_result_error< ::boost::math::policies::errno_on_error>&) | |
{ | |
errno = EDOM; | |
// This may or may not do the right thing, but the user asked for the error | |
// to be silent so here we go anyway: | |
return result; | |
} | |
template <class T, class R> | |
inline T raise_indeterminate_result_error( | |
const char* function, | |
const char* message, | |
const T& val, | |
const R& , | |
const ::boost::math::policies::indeterminate_result_error< ::boost::math::policies::user_error>&) | |
{ | |
return user_indeterminate_result_error(function, message, val); | |
} | |
} // namespace detail | |
template <class T, class Policy> | |
inline T raise_domain_error(const char* function, const char* message, const T& val, const Policy&) | |
{ | |
typedef typename Policy::domain_error_type policy_type; | |
return detail::raise_domain_error( | |
function, message ? message : "Domain Error evaluating function at %1%", | |
val, policy_type()); | |
} | |
template <class T, class Policy> | |
inline T raise_pole_error(const char* function, const char* message, const T& val, const Policy&) | |
{ | |
typedef typename Policy::pole_error_type policy_type; | |
return detail::raise_pole_error( | |
function, message ? message : "Evaluation of function at pole %1%", | |
val, policy_type()); | |
} | |
template <class T, class Policy> | |
inline T raise_overflow_error(const char* function, const char* message, const Policy&) | |
{ | |
typedef typename Policy::overflow_error_type policy_type; | |
return detail::raise_overflow_error<T>( | |
function, message ? message : "Overflow Error", | |
policy_type()); | |
} | |
template <class T, class Policy> | |
inline T raise_underflow_error(const char* function, const char* message, const Policy&) | |
{ | |
typedef typename Policy::underflow_error_type policy_type; | |
return detail::raise_underflow_error<T>( | |
function, message ? message : "Underflow Error", | |
policy_type()); | |
} | |
template <class T, class Policy> | |
inline T raise_denorm_error(const char* function, const char* message, const T& val, const Policy&) | |
{ | |
typedef typename Policy::denorm_error_type policy_type; | |
return detail::raise_denorm_error<T>( | |
function, message ? message : "Denorm Error", | |
val, | |
policy_type()); | |
} | |
template <class T, class Policy> | |
inline T raise_evaluation_error(const char* function, const char* message, const T& val, const Policy&) | |
{ | |
typedef typename Policy::evaluation_error_type policy_type; | |
return detail::raise_evaluation_error( | |
function, message ? message : "Internal Evaluation Error, best value so far was %1%", | |
val, policy_type()); | |
} | |
template <class T, class TargetType, class Policy> | |
inline T raise_rounding_error(const char* function, const char* message, const T& val, const TargetType& t, const Policy&) | |
{ | |
typedef typename Policy::rounding_error_type policy_type; | |
return detail::raise_rounding_error( | |
function, message ? message : "Value %1% can not be represented in the target integer type.", | |
val, t, policy_type()); | |
} | |
template <class T, class R, class Policy> | |
inline T raise_indeterminate_result_error(const char* function, const char* message, const T& val, const R& result, const Policy&) | |
{ | |
typedef typename Policy::indeterminate_result_error_type policy_type; | |
return detail::raise_indeterminate_result_error( | |
function, message ? message : "Indeterminate result with value %1%", | |
val, result, policy_type()); | |
} | |
// | |
// checked_narrowing_cast: | |
// | |
namespace detail | |
{ | |
template <class R, class T, class Policy> | |
inline bool check_overflow(T val, R* result, const char* function, const Policy& pol) | |
{ | |
BOOST_MATH_STD_USING | |
if(fabs(val) > tools::max_value<R>()) | |
{ | |
*result = static_cast<R>(boost::math::policies::detail::raise_overflow_error<R>(function, 0, pol)); | |
return true; | |
} | |
return false; | |
} | |
template <class R, class T, class Policy> | |
inline bool check_underflow(T val, R* result, const char* function, const Policy& pol) | |
{ | |
if((val != 0) && (static_cast<R>(val) == 0)) | |
{ | |
*result = static_cast<R>(boost::math::policies::detail::raise_underflow_error<R>(function, 0, pol)); | |
return true; | |
} | |
return false; | |
} | |
template <class R, class T, class Policy> | |
inline bool check_denorm(T val, R* result, const char* function, const Policy& pol) | |
{ | |
BOOST_MATH_STD_USING | |
if((fabs(val) < static_cast<T>(tools::min_value<R>())) && (static_cast<R>(val) != 0)) | |
{ | |
*result = static_cast<R>(boost::math::policies::detail::raise_denorm_error<R>(function, 0, static_cast<R>(val), pol)); | |
return true; | |
} | |
return false; | |
} | |
// Default instantiations with ignore_error policy. | |
template <class R, class T> | |
inline bool check_overflow(T /* val */, R* /* result */, const char* /* function */, const overflow_error<ignore_error>&){ return false; } | |
template <class R, class T> | |
inline bool check_underflow(T /* val */, R* /* result */, const char* /* function */, const underflow_error<ignore_error>&){ return false; } | |
template <class R, class T> | |
inline bool check_denorm(T /* val */, R* /* result*/, const char* /* function */, const denorm_error<ignore_error>&){ return false; } | |
} // namespace detail | |
template <class R, class Policy, class T> | |
inline R checked_narrowing_cast(T val, const char* function) | |
{ | |
typedef typename Policy::overflow_error_type overflow_type; | |
typedef typename Policy::underflow_error_type underflow_type; | |
typedef typename Policy::denorm_error_type denorm_type; | |
// | |
// Most of what follows will evaluate to a no-op: | |
// | |
R result; | |
if(detail::check_overflow<R>(val, &result, function, overflow_type())) | |
return result; | |
if(detail::check_underflow<R>(val, &result, function, underflow_type())) | |
return result; | |
if(detail::check_denorm<R>(val, &result, function, denorm_type())) | |
return result; | |
return static_cast<R>(val); | |
} | |
template <class Policy> | |
inline void check_series_iterations(const char* function, boost::uintmax_t max_iter, const Policy& pol) | |
{ | |
if(max_iter >= policies::get_max_series_iterations<Policy>()) | |
raise_evaluation_error<boost::uintmax_t>( | |
function, | |
"Series evaluation exceeded %1% iterations, giving up now.", max_iter, pol); | |
} | |
template <class Policy> | |
inline void check_root_iterations(const char* function, boost::uintmax_t max_iter, const Policy& pol) | |
{ | |
if(max_iter >= policies::get_max_root_iterations<Policy>()) | |
raise_evaluation_error<boost::uintmax_t>( | |
function, | |
"Root finding evaluation exceeded %1% iterations, giving up now.", max_iter, pol); | |
} | |
} //namespace policies | |
#ifdef BOOST_MSVC | |
# pragma warning(pop) | |
#endif | |
}} // namespaces boost/math | |
#endif // BOOST_MATH_POLICY_ERROR_HANDLING_HPP | |