C
Mit traits sieht es evtl. so aus:
#include <cmath>
#include <limits>
#include <utility>
template <typename T, typename = typename std::enable_if<std::numeric_limits<T>::is_specialized && std::numeric_limits<T>::has_quiet_NaN>::type>
bool my_isnan(const T& v)
{
using std::isnan;
return isnan( v );
}
template <typename T, bool hack = true, typename = typename std::enable_if<std::numeric_limits<T>::is_specialized && !std::numeric_limits<T>::has_quiet_NaN>::type>
constexpr bool my_isnan(const T&)
{
return true;
}
template <typename T, typename = typename std::enable_if<std::numeric_limits<T>::is_specialized && std::numeric_limits<T>::has_infinity>::type>
bool my_isinf(const T& v)
{
using std::isinf;
return isinf( v );
}
template <typename T, bool hack = true, typename = typename std::enable_if<std::numeric_limits<T>::is_specialized && !std::numeric_limits<T>::has_infinity>::type>
constexpr bool my_isinf(const T&)
{
return false;
}
template <typename T, typename = typename std::enable_if<std::numeric_limits<T>::is_specialized>::type>
constexpr bool my_iszero(const T& v)
{
return v == T();
}
template <typename T, typename U>
auto my_save_div(T&& lhs, U&& rhs) -> decltype( lhs / rhs )
{
return my_isnan(lhs) || my_isnan(rhs) ? throw "NaN"
: my_isinf(lhs) && my_isinf(rhs) ? throw "inf//inf"
: my_iszero(rhs) ? throw "//0"
: std::forward<T>( lhs ) / std::forward<U>( rhs );
}
(hack dient nur dazu, die Überladungen für die ODR auseinanderzuhalten, weil Defaultargumente nicht Teil der Signatur sind.)
Ob das dann Sinn macht ist eine anderer Frage und hängt davon ab, was man als "safe" betrachtet. Schließlich kann es auch hier passieren, dass plötzlich Unendlichkeiten (und dann in Folgeberechnungen NaNs) entstehen, weil das Ergebnis zu groß für eine endliche Darstellung ist.