maths, view: rationalise equal,almost_equal,==

views should not do elementwise comparisons for equality operators.
they are pairs of iterators and are only equal if their iterators are
equal.

instead, use `equal` for elementwise equality. we update the name of
exactly_equal to perform this operation too.
This commit is contained in:
Danny Robson 2018-01-31 19:33:42 +11:00
parent 5d32408126
commit 35e3f69ad2
10 changed files with 78 additions and 91 deletions

View File

@ -909,7 +909,7 @@ json::tree::number::sint (void) const
return m_value.s; return m_value.s;
case REAL: case REAL:
if (!::util::exactly_equal (real_t (sint_t (m_value.r)), m_value.r)) if (!::util::equal (real_t (sint_t (m_value.r)), m_value.r))
throw type_error ("number is not a sint"); throw type_error ("number is not a sint");
return sint_t (m_value.r); return sint_t (m_value.r);
@ -932,7 +932,7 @@ json::tree::number::uint (void) const
return m_value.u; return m_value.u;
case REAL: case REAL:
if (!::util::exactly_equal (real_t (uint_t (m_value.r)), m_value.r)) if (!::util::equal (real_t (uint_t (m_value.r)), m_value.r))
throw type_error ("number is not a uint"); throw type_error ("number is not a uint");
return uint_t (m_value.r); return uint_t (m_value.r);

114
maths.hpp
View File

@ -43,67 +43,6 @@
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
namespace util { namespace util {
///////////////////////////////////////////////////////////////////////////
// Comparisons
inline bool
almost_equal (const float &a, const float &b)
{
return ieee_single::almost_equal (a, b);
}
//-----------------------------------------------------------------------------
inline bool
almost_equal (const double &a, const double &b)
{
return ieee_double::almost_equal (a, b);
}
//-----------------------------------------------------------------------------
template <typename A, typename B>
inline
typename std::enable_if_t<
std::is_floating_point<A>::value &&
std::is_floating_point<B>::value &&
!std::is_same<A,B>::value ,
bool
>
almost_equal (const A &a, const B &b)
{
using common_t = std::common_type_t<A,B>;
return almost_equal<common_t> (static_cast<common_t> (a),
static_cast<common_t> (b));
}
//-----------------------------------------------------------------------------
template <typename A, typename B>
inline
typename std::enable_if_t<
std::is_integral_v<A> &&
std::is_integral_v<B> &&
std::is_signed<A>::value == std::is_signed<B>::value,
bool
>
almost_equal (const A &a, const B &b) {
using common_t = std::common_type_t<A,B>;
return static_cast<common_t> (a) == static_cast<common_t> (b);
}
//-----------------------------------------------------------------------------
template <typename Ta, typename Tb>
constexpr inline
typename std::enable_if<
!std::is_arithmetic<Ta>::value ||
!std::is_arithmetic<Tb>::value,
bool
>::type
almost_equal (const Ta &a, const Tb &b)
{ return a == b; }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Useful for explictly ignore equality warnings // Useful for explictly ignore equality warnings
#pragma GCC diagnostic push #pragma GCC diagnostic push
@ -115,7 +54,7 @@ namespace util {
std::is_arithmetic<Tb>::value, std::is_arithmetic<Tb>::value,
bool bool
> >
exactly_equal (const Ta &a, const Tb &b) equal (const Ta &a, const Tb &b)
{ {
return a == b; return a == b;
} }
@ -128,13 +67,50 @@ namespace util {
!std::is_arithmetic<Tb>::value, !std::is_arithmetic<Tb>::value,
bool bool
> >
exactly_equal (const Ta &a, const Tb &b) equal (const Ta &a, const Tb &b)
{ {
return a == b; return a == b;
} }
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
///////////////////////////////////////////////////////////////////////////
// Comparisons
inline bool
almost_equal (float a, float b)
{
return ieee_single::almost_equal (a, b);
}
//-----------------------------------------------------------------------------
inline bool
almost_equal (double a, double b)
{
return ieee_double::almost_equal (a, b);
}
//-----------------------------------------------------------------------------
template <typename ValueA, typename ValueB>
constexpr auto
almost_equal (const ValueA &a, const ValueB &b)
{
if constexpr (std::is_floating_point_v<ValueA> && std::is_floating_point_v<ValueB>) {
using common_t = std::common_type_t<ValueA,ValueB>;
return almost_equal (common_t {a}, common_t{b});
} else if constexpr (std::is_integral_v<ValueA> &&
std::is_integral_v<ValueB> &&
std::is_signed_v<ValueA> == std::is_signed_v<ValueB>)
{
using common_t = std::common_type_t<ValueA,ValueB>;
return static_cast<common_t> (a) == static_cast<common_t> (b);
} else {
return equal (a, b);
}
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
template <typename T> template <typename T>
constexpr constexpr
@ -146,12 +122,16 @@ namespace util {
return t == 0; return t == 0;
} }
//-------------------------------------------------------------------------
template <typename T> template <typename T>
std::enable_if_t< std::enable_if_t<
!std::is_integral<T>::value, bool !std::is_integral<T>::value, bool
> >
almost_zero (T a) almost_zero (T a)
{ return almost_equal (a, T{0}); } {
return almost_equal (a, T{0});
}
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
@ -162,7 +142,7 @@ namespace util {
> >
exactly_zero (T t) exactly_zero (T t)
{ {
return exactly_equal (t, T{0}); return equal (t, T{0});
} }
@ -173,7 +153,7 @@ namespace util {
> >
exactly_zero (T t) exactly_zero (T t)
{ {
return exactly_equal (t, T{0}); return equal (t, T{0});
} }
@ -337,7 +317,7 @@ namespace util {
is_integer (T t) is_integer (T t)
{ {
T i = 0; T i = 0;
return exactly_equal (std::modf (t, &i), T{0}); return equal (std::modf (t, &i), T{0});
} }

View File

@ -126,7 +126,7 @@ matrix<Rows,Cols,T>::is_affine (void) const
if (!exactly_zero (values[Rows-1][i])) if (!exactly_zero (values[Rows-1][i]))
return false; return false;
return exactly_equal (values[Rows-1][Rows-1], T{1}); return equal (values[Rows-1][Rows-1], T{1});
} }

View File

@ -38,7 +38,7 @@ test_double (util::TAP::logger &tap)
util::ieee_double val; util::ieee_double val;
val.set_bits (tests[i].bits); val.set_bits (tests[i].bits);
success = success && util::exactly_equal (val, tests[i].floating); success = success && util::equal (val, tests[i].floating);
} }
tap.expect (success, "double precision bitwise equality"); tap.expect (success, "double precision bitwise equality");
@ -74,7 +74,7 @@ test_single (util::TAP::logger &tap)
util::ieee_single val; util::ieee_single val;
val.set_bits (tests[i].bits); val.set_bits (tests[i].bits);
success = success && util::exactly_equal (val, tests[i].floating); success = success && util::equal (val, tests[i].floating);
} }
tap.expect (success, "single precision bitwise equality"); tap.expect (success, "single precision bitwise equality");

View File

@ -46,8 +46,8 @@ int main ()
foo d_foo { 7, 42.0 }; foo d_foo { 7, 42.0 };
auto f_tuple = util::as_tuple (d_foo); auto f_tuple = util::as_tuple (d_foo);
tap.expect (util::exactly_equal (d_foo.a, std::get<0> (f_tuple)) && tap.expect (util::equal (d_foo.a, std::get<0> (f_tuple)) &&
util::exactly_equal (d_foo.b, std::get<1> (f_tuple)), util::equal (d_foo.b, std::get<1> (f_tuple)),
"dynamic member access after conversion to tuple"); "dynamic member access after conversion to tuple");
} }

View File

@ -20,7 +20,7 @@ main (int, char**)
for (const auto &[i, v, a, c]: util::izip (v_int, a_float, c_char)) { for (const auto &[i, v, a, c]: util::izip (v_int, a_float, c_char)) {
success = success && success = success &&
v_int[i] == v && v_int[i] == v &&
util::exactly_equal (a_float[i], a) && util::equal (a_float[i], a) &&
c_char[i] == c; c_char[i] == c;
} }

View File

@ -53,7 +53,7 @@ main (void)
tap.expect (!ref["integer"].is_object (), "integer not is_object"); tap.expect (!ref["integer"].is_object (), "integer not is_object");
tap.expect (!ref["integer"].is_string (), "integer not is_string"); tap.expect (!ref["integer"].is_string (), "integer not is_string");
tap.expect ( tap.expect (
util::exactly_equal ( util::equal (
(unsigned)ref["integer"].as_number ().as_uint (), (unsigned)ref["integer"].as_number ().as_uint (),
1u 1u
), ),
@ -101,7 +101,7 @@ main (void)
tap.expect (!ref["double"].is_object (), "double not is_object"); tap.expect (!ref["double"].is_object (), "double not is_object");
tap.expect (!ref["double"].is_string (), "double not is_string"); tap.expect (!ref["double"].is_string (), "double not is_string");
tap.expect ( tap.expect (
util::exactly_equal ( util::equal (
ref["double"].as_number ().as<double> (), ref["double"].as_number ().as<double> (),
3.14 3.14
), ),

View File

@ -69,10 +69,10 @@ main (void)
const point4f q = p.homog<4> (); const point4f q = p.homog<4> ();
tap.expect ( tap.expect (
almost_equal (q.x, 3.f) && equal (q.x, 3.f) &&
almost_equal (q.y, 4.f) && equal (q.y, 4.f) &&
almost_equal (q.z, 0.f) && equal (q.z, 0.f) &&
almost_equal (q.w, 1.f), equal (q.w, 1.f),
"homogenous redim" "homogenous redim"
); );

View File

@ -29,11 +29,9 @@ main (int, char**)
{ "", "trailing empty" } { "", "trailing empty" }
}; };
for (const auto tok: util::tokeniser (csv.c_str (), ',')) util::view src { csv.c_str (), csv.size () };
std::cout << '"' << tok << "\"\n"; for (const auto &[tok, expected]: util::zip (util::tokeniser (src, ','), TESTS))
tap.expect (equal (tok, expected.value), "%s", expected.message);
for (const auto &[tok, expected]: util::zip (util::tokeniser (csv.c_str (), ','), TESTS))
tap.expect_eq (tok, expected.value, "%s", expected.message);
return tap.status (); return tap.status ();
} }

View File

@ -589,7 +589,7 @@ namespace util {
typename BeginB, typename EndB typename BeginB, typename EndB
> >
constexpr bool constexpr bool
operator== (const view<BeginA,EndA> &a, const view<BeginB,EndB> &b) equal (const view<BeginA,EndA> &a, const view<BeginB,EndB> &b)
{ {
return a.size () == b.size () && return a.size () == b.size () &&
std::equal (std::begin (a), std::end (a), std::begin (b)); std::equal (std::begin (a), std::end (a), std::begin (b));
@ -608,9 +608,9 @@ namespace util {
> >
> >
constexpr bool constexpr bool
operator== (const view<IteratorA,IteratorB> &a, const ValueT &b) equal (const view<IteratorA,IteratorB> &a, const ValueT &b)
{ {
return a == make_view (b); return equal (a, make_view (b));
} }
@ -628,15 +628,24 @@ namespace util {
> >
> >
constexpr bool constexpr bool
operator== (const ValueT &a, const view<IteratorA,IteratorB> &b) equal (const ValueT &a, const view<IteratorA,IteratorB> &b)
{ {
return b == a; return equal (b, a);
} }
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
template <typename IteratorA, typename IteratorB> template <typename IteratorA, typename IteratorB>
constexpr bool constexpr bool
operator== (const view<IteratorA,IteratorB> &a, const view<IteratorA,IteratorB> &b)
{
return a.begin () == b.begin () && a.end () == b.end ();
}
//-------------------------------------------------------------------------
template <typename IteratorA, typename IteratorB>
constexpr bool
operator!= (const view<IteratorA,IteratorB> &a, const view<IteratorA,IteratorB> &b) operator!= (const view<IteratorA,IteratorB> &a, const view<IteratorA,IteratorB> &b)
{ {
return !(a == b); return !(a == b);