sticky question regarding boost rational

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

sticky question regarding boost rational

Boost - Dev mailing list
boost rational is tripping up one of my examples in safe numerics:

     // solution: use safe integer in rational definition
     using safe_rational = boost::rational<
         boost::safe_numerics::safe<int>
     >;

     // use rationals created with safe_t
     const safe_rational sc {1, std::numeric_limits<int>::max()};

     std::cout << "c = " << sc << std::endl;
     const safe_rational sd {1, 2};
     std::cout << "d = " << sd << std::endl;
     std::cout << "c * d = ";
     try {
         // multiply them. This will overflow
         std::cout << sc * sd << std::endl;   // use of overload
operator * is ambiguous.

rational.hpp contains - among other things, the following definitions
for the * operator:

template <class IntType, class Arg>
BOOST_CXX14_CONSTEXPR
inline typename boost::enable_if_c <
    rational_detail::is_compatible_integer<Arg, IntType>::value ||
is_same<rational<IntType>, Arg>::value, rational<IntType> >::type
    operator * (const rational<IntType>& a, const Arg& b)
{
       rational<IntType> t(a);
       return t *= b;
}
template <class Arg, class IntType>
BOOST_CXX14_CONSTEXPR
inline typename boost::enable_if_c <
    rational_detail::is_compatible_integer<Arg, IntType>::value,
rational<IntType> >::type
    operator * (const Arg& b, const rational<IntType>& a)
{
       rational<IntType> t(a);
       return t *= b;
}

Soooooo - it seems that sc * sd will match both of the above
definitions.  Its unclear what the purpose of these two different
overloads are.  They look pretty similar to me.

the definition for is_compatible_integer.

namespace rational_detail{

    template <class FromInt, class ToInt, typename Enable = void>
    struct is_compatible_integer;

    template <class FromInt, class ToInt>
    struct is_compatible_integer<FromInt, ToInt, typename
enable_if_c<!is_array<FromInt>::value>::type>
    {
       BOOST_STATIC_CONSTANT(bool, value =
((std::numeric_limits<FromInt>::is_specialized
        && std::numeric_limits<FromInt>::is_integer
          && (std::numeric_limits<FromInt>::digits
<=std::numeric_limits<ToInt>::digits)
          && (std::numeric_limits<FromInt>::radix ==
std::numeric_limits<ToInt>::radix)
          && ((std::numeric_limits<FromInt>::is_signed == false) ||
(std::numeric_limits<ToInt>::is_signed == true))
          && is_convertible<FromInt, ToInt>::value)
          || is_same<FromInt, ToInt>::value)
          || (is_class<ToInt>::value && is_class<FromInt>::value &&
is_convertible<FromInt, ToInt>::value));
    };
...

In fact, since it looks like if FromInt and ToInt are the same types (as
they are in my case, one will always got more than one match.  Is anyone
(John Maddock - where are you?) able to shed some light on this for me?

Robert Ramey


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: sticky question regarding boost rational

Boost - Dev mailing list
On Sat, 31 Oct 2020, Robert Ramey via Boost wrote:

> boost rational is tripping up one of my examples in safe numerics:
>
>    // solution: use safe integer in rational definition
>    using safe_rational = boost::rational<
>        boost::safe_numerics::safe<int>
>    >;
>
>    // use rationals created with safe_t
>    const safe_rational sc {1, std::numeric_limits<int>::max()};
>
>    std::cout << "c = " << sc << std::endl;
>    const safe_rational sd {1, 2};
>    std::cout << "d = " << sd << std::endl;
>    std::cout << "c * d = ";
>    try {
>        // multiply them. This will overflow
>        std::cout << sc * sd << std::endl;   // use of overload operator * is
> ambiguous.

So you are multiplying 2 rationals, you could have made the example more
minimal with sd * sd.

> rational.hpp contains - among other things, the following definitions for the
> * operator:
>
> template <class IntType, class Arg>
> BOOST_CXX14_CONSTEXPR
> inline typename boost::enable_if_c <
>   rational_detail::is_compatible_integer<Arg, IntType>::value ||
> is_same<rational<IntType>, Arg>::value, rational<IntType> >::type
>   operator * (const rational<IntType>& a, const Arg& b)
> {
>      rational<IntType> t(a);
>      return t *= b;
> }
> template <class Arg, class IntType>
> BOOST_CXX14_CONSTEXPR
> inline typename boost::enable_if_c <
>   rational_detail::is_compatible_integer<Arg, IntType>::value,
> rational<IntType> >::type
>   operator * (const Arg& b, const rational<IntType>& a)
> {
>      rational<IntType> t(a);
>      return t *= b;
> }
>
> Soooooo - it seems that sc * sd will match both of the above definitions.
> Its unclear what the purpose of these two different overloads are.  They look
> pretty similar to me.

They look very different. The first one is for rational * integer or
rational * rational, and the second one for integer * rational.

> the definition for is_compatible_integer.
>
> namespace rational_detail{
>
>   template <class FromInt, class ToInt, typename Enable = void>
>   struct is_compatible_integer;
>
>   template <class FromInt, class ToInt>
>   struct is_compatible_integer<FromInt, ToInt, typename
> enable_if_c<!is_array<FromInt>::value>::type>
>   {
>      BOOST_STATIC_CONSTANT(bool, value =
> ((std::numeric_limits<FromInt>::is_specialized
> && std::numeric_limits<FromInt>::is_integer
>         && (std::numeric_limits<FromInt>::digits
> <=std::numeric_limits<ToInt>::digits)
>         && (std::numeric_limits<FromInt>::radix ==
> std::numeric_limits<ToInt>::radix)
>         && ((std::numeric_limits<FromInt>::is_signed == false) ||
> (std::numeric_limits<ToInt>::is_signed == true))
>         && is_convertible<FromInt, ToInt>::value)
>         || is_same<FromInt, ToInt>::value)
>         || (is_class<ToInt>::value && is_class<FromInt>::value &&
> is_convertible<FromInt, ToInt>::value));
>   };
> ...
>
> In fact, since it looks like if FromInt and ToInt are the same types (as they
> are in my case, one will always got more than one match.

AFAICT, Arg is rational in your case, so it is **not** the same as
IntType. And I hope that rational is not convertible to safe<int>,
although a badly constrained constructor there could easily cause trouble.

--
Marc Glisse

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost