Defining functions for all integers available on the platform

I would like to write a family of functions for various integer types INT, whose signature

INT safe_product(INT a, INT b, bool& error);

which takes two integers a and b and returns a * b if a * b does not overflow and returns 0 and sets the error to true if a * b overflows. I also want this feature to be effective, and I want it to run on 32-bit and 64-bit platforms.

I think overload safe_product using the std::int32_t, std::uint32_t, std::int64_t, std::uint64_tand etc. I believe that it is std::int64_tnot always defined with a 32-bit compiler. Is there a way to find out at compile time, if defined?

In addition, if we are on a 64-bit plate, the best way to implement a secure product between two 32-bit integers is:

std::int32_t safe_product(std::int32_t a, std::int32_t b,
                          bool& error) {
  const std::int64_t a_64 = a;
  const std::int64_t b_64 = b;
  const std::int64_t ab_64 = a_64 * b_64;
  if (ab_64 > std::numeric_limits<std::int32_t>::max() ||
      ab_64 < std::numeric_limits<std::int32_t>::min()) {
    error = true;
    return 0;
  } else {
    error = false;
    return static_cast<std::int32_t>(ab_64);
  }
}

but if we are a 32-bit platform, the fastest algorithm may mean computing some integer division.

I have 2 questions:

  • How to declare mine safe_productso that it is defined for all integer types available on my platform (and obviously not for those that don't exist)?

  • How to make it efficient for both 32-bit and 64-bit using algorithms that I know?

+4
source share
1 answer

- . int_fastXX_t, , . void* , . int unsigned int .

" " , , . " " , . , , .

#include <cstdint>
#include <limits>
#include <type_traits>

// Define the fastest types for our case
using t_fast_int = int;
using t_fast_uint = unsigned int;

// Helper trait, to indicate if a type is small enough to promote
template<class T>
struct t_is_small : std::bool_constant<sizeof(T) < sizeof(t_fast_int)> {};

-, enable_if ([link (http://en.cppreference.com/w/cpp/types/enable_if)), , , .

template<class T>
std::enable_if_t<t_is_small<T>::value, T>
safe_product(T a, T b, bool& error)
{
    // Should we use intmax_t or uintmax_t in this case?
    using t_large = std::conditional_t<std::is_signed<T>::value, t_fast_int, t_fast_uint>;

    const t_large a_64 = a;
    const t_large b_64 = b;
    const t_large ab_64 = a_64 * b_64;
    if (ab_64 > std::numeric_limits<T>::max() ||
        ab_64 < std::numeric_limits<T>::min())
    {
        error = true;
        return 0;
    }
    else
    {
        error = false;
        return static_cast<T>(ab_64);
    }
}

, . , enable_if . .

template<class T>
std::enable_if_t<t_is_small<T>::value == false, T>
safe_product(T a, T b, bool& error)
{
    if(b == 0) {
        // The result will be zero (avoids division by zero below)
        error = false;
    }
    else {
        // Calculate the largest `a` that would not result in an overflow
        constexpr auto max_int = std::numeric_limits<T>::max();
        auto max_a = max_int / b;

        // Calculate the smallest `a` that would not result in underflow
        constexpr auto min_int = std::numeric_limits<T>::min();
        auto min_a = min_int / b;

        // If a is greater than max_a an overflow would occur
        // If a is less than min_a an undeflow would occur
        if(b > 0) {
            error = (a > max_a) || (a < min_a);
        }
        else {
            error = (a < max_a) || (a > min_a);
        }
    }
    if(error) {
        return 0;
    }
    else {
        return a * b;
    }
}
+2

Source: https://habr.com/ru/post/1667974/


All Articles