Информация об изменениях

Сообщение Re: примерный результат (переполнения целочисленных) от 23.08.2022 8:24

Изменено 23.08.2022 8:30 Sm0ke

Re: примерный результат (переполнения целочисленных)
Всем спасибо за ответы. У меня получился примерно такой код:
https://godbolt.org/z/PKGeYj3x3

Ещё добавил деление и modulo (%) — остаток от деления. При делении будет целый результат, только если делится на-цело.
Так-же учитывается int_min / (-1), при котором результат будет с плавающей точкой.

#include <iostream>
#include <iomanip>
#include <limits>
#include <cstdint>
#include <cmath>
#include <variant>

template <typename T_int, typename T_float>
struct safe {
  using t_int = T_int;
  using t_float = T_float;
  using t_limits_int = std::numeric_limits<t_int>;
  using t_limits_float = std::numeric_limits<t_float>;
  using t_mono = std::monostate;
  using t_var = std::variant<t_mono, t_int, t_float>;

  static constexpr t_int s_int_min = t_limits_int::min();
  static constexpr t_int s_int_max = t_limits_int::max();

  struct op_plus {
    static bool allow(t_int p1, t_int p2) {
      return (p2 >= 0) ? (p1 <= s_int_max - p2) : (p1 >= s_int_min - p2);
    }
    static t_int with_int(t_int p1, t_int p2) { return p1 + p2; }
    static t_float with_float(t_float p1, t_float p2) { return p1 + p2; }
  };

  struct op_minus {
    static bool allow(t_int p1, t_int p2) {
      return (p2 >= 0) ? (p1 >= s_int_min + p2) : (p1 <= s_int_max + p2);
    }
    static t_int with_int(t_int p1, t_int p2) { return p1 - p2; }
    static t_float with_float(t_float p1, t_float p2) { return p1 - p2; }
  };

  struct op_mult {
    static bool allow(t_int p1, t_int p2) {
      return (p1 == 0) || (p2 == 0) || ((p1 > 0) ? (
        (p2 > 0) ? (p1 <= s_int_max / p2) : (p2 != -1 && p1 <= s_int_min / p2)
      ) : (
        (p2 > 0) ? (p1 >= s_int_min / p2) : (p1 >= s_int_max / p2)
      ));
    }
    static t_int with_int(t_int p1, t_int p2) { return p1 * p2; }
    static t_float with_float(t_float p1, t_float p2) { return p1 * p2; }
  };

  struct op_div {
    static bool allow(t_int p1, t_int p2) {
      return (p2 == -1) ? (p1 > s_int_min) : (p2 != 0 && (p1 % p2 == 0));
    }
    static t_int with_int(t_int p1, t_int p2) { return p1 / p2; }
    static t_float with_float(t_float p1, t_float p2) { return p1 / p2; }
  };

  struct op_mod {
    static bool allow(t_int p1, t_int p2) {
      return p2 != 0;
    }
    static t_int with_int(t_int p1, t_int p2) {
      if( p2 == -1 ) { return 0; }
      auto v_res = std::div(p1, p2);
      return v_res.rem;
    }
    static t_float with_float(t_float p1, t_float p2) { return std::remainder(p1, p2); }
  };

  template <typename T_op>
  struct visitor_math {
    using type = T_op;

    t_var operator () (t_int p1, t_int p2) {
      if( type::allow(p1, p2) ) { return type::with_int(p1, p2); }
      return type::with_float( static_cast<t_float>(p1), static_cast<t_float>(p2) );
    }
    t_var operator () (t_float p1, t_float p2) { return type::with_float(p1, p2); }

    t_var operator () (t_int p1, t_float p2) {
      return type::with_float( static_cast<t_float>(p1), p2 );
    }
    t_var operator () (t_float p1, t_int p2) {
      return type::with_float( p1, static_cast<t_float>(p2) );
    }

    template <typename T1, typename T2>
    t_var operator () (T1 p1, T2 p2) { return t_mono{}; }
  };

  static t_var plus(const t_var & p1, const t_var & p2) {
    return std::visit<t_var>(visitor_math<op_plus>{}, p1, p2);
  }
  static t_var minus(const t_var & p1, const t_var & p2) {
    return std::visit<t_var>(visitor_math<op_minus>{}, p1, p2);
  }
  static t_var mult(const t_var & p1, const t_var & p2) {
    return std::visit<t_var>(visitor_math<op_mult>{}, p1, p2);
  }
  static t_var div(const t_var & p1, const t_var & p2) {
    return std::visit<t_var>(visitor_math<op_div>{}, p1, p2);
  }
  static t_var mod(const t_var & p1, const t_var & p2) {
    return std::visit<t_var>(visitor_math<op_mod>{}, p1, p2);
  }

  struct visitor_print {
    void operator () (t_mono) { std::cout << "null"; }
    void operator () (t_int p) { std::cout << p; }
    void operator () (t_float p) { std::cout << p; }
  };

  static void print(const t_var & p) {
    std::visit<void>(visitor_print{}, p);
  }
};

int main() {
  using t_safe = safe<std::intmax_t, long double>;
  using t_var = t_safe::t_var;
  t_var v1 = t_safe::s_int_min, v2 = -1, v3 = t_safe::t_mono{};
  t_var v_res = t_safe::mod(v1, v2);
  std::cout << std::setprecision(t_safe::t_limits_float::digits10);
  t_safe::print(v_res);
  std::cout << '\n';
  v_res = t_safe::plus(v1, v3);
  t_safe::print(v_res);
  std::cout << '\n';
  return 0;
}


Но в моём реальном проекте код немного другой.
Re: примерный результат (переполнения целочисленных)
Всем спасибо за ответы. У меня получился примерно такой код:
https://godbolt.org/z/PKGeYj3x3

Ещё добавил деление и modulo (%) — остаток от деления. При делении будет целый результат, только если делится на-цело.
Так-же учитывается int_min / (-1), при котором результат будет с плавающей точкой.

Используется std::variant, где std::monostate — это специальное состояние, математические операции с которым дают тоже monostate.
using t_mono = std::monostate;
using t_var = std::variant<t_mono, t_int, t_float>;

#include <iostream>
#include <iomanip>
#include <limits>
#include <cstdint>
#include <cmath>
#include <variant>

template <typename T_int, typename T_float>
struct safe {
  using t_int = T_int;
  using t_float = T_float;
  using t_limits_int = std::numeric_limits<t_int>;
  using t_limits_float = std::numeric_limits<t_float>;
  using t_mono = std::monostate;
  using t_var = std::variant<t_mono, t_int, t_float>;

  static constexpr t_int s_int_min = t_limits_int::min();
  static constexpr t_int s_int_max = t_limits_int::max();

  struct op_plus {
    static bool allow(t_int p1, t_int p2) {
      return (p2 >= 0) ? (p1 <= s_int_max - p2) : (p1 >= s_int_min - p2);
    }
    static t_int with_int(t_int p1, t_int p2) { return p1 + p2; }
    static t_float with_float(t_float p1, t_float p2) { return p1 + p2; }
  };

  struct op_minus {
    static bool allow(t_int p1, t_int p2) {
      return (p2 >= 0) ? (p1 >= s_int_min + p2) : (p1 <= s_int_max + p2);
    }
    static t_int with_int(t_int p1, t_int p2) { return p1 - p2; }
    static t_float with_float(t_float p1, t_float p2) { return p1 - p2; }
  };

  struct op_mult {
    static bool allow(t_int p1, t_int p2) {
      return (p1 == 0) || (p2 == 0) || ((p1 > 0) ? (
        (p2 > 0) ? (p1 <= s_int_max / p2) : (p2 != -1 && p1 <= s_int_min / p2)
      ) : (
        (p2 > 0) ? (p1 >= s_int_min / p2) : (p1 >= s_int_max / p2)
      ));
    }
    static t_int with_int(t_int p1, t_int p2) { return p1 * p2; }
    static t_float with_float(t_float p1, t_float p2) { return p1 * p2; }
  };

  struct op_div {
    static bool allow(t_int p1, t_int p2) {
      return (p2 == -1) ? (p1 > s_int_min) : (p2 != 0 && (p1 % p2 == 0));
    }
    static t_int with_int(t_int p1, t_int p2) { return p1 / p2; }
    static t_float with_float(t_float p1, t_float p2) { return p1 / p2; }
  };

  struct op_mod {
    static bool allow(t_int p1, t_int p2) {
      return p2 != 0;
    }
    static t_int with_int(t_int p1, t_int p2) {
      if( p2 == -1 ) { return 0; }
      auto v_res = std::div(p1, p2);
      return v_res.rem;
    }
    static t_float with_float(t_float p1, t_float p2) { return std::remainder(p1, p2); }
  };

  template <typename T_op>
  struct visitor_math {
    using type = T_op;

    t_var operator () (t_int p1, t_int p2) {
      if( type::allow(p1, p2) ) { return type::with_int(p1, p2); }
      return type::with_float( static_cast<t_float>(p1), static_cast<t_float>(p2) );
    }
    t_var operator () (t_float p1, t_float p2) { return type::with_float(p1, p2); }

    t_var operator () (t_int p1, t_float p2) {
      return type::with_float( static_cast<t_float>(p1), p2 );
    }
    t_var operator () (t_float p1, t_int p2) {
      return type::with_float( p1, static_cast<t_float>(p2) );
    }

    template <typename T1, typename T2>
    t_var operator () (T1 p1, T2 p2) { return t_mono{}; }
  };

  static t_var plus(const t_var & p1, const t_var & p2) {
    return std::visit<t_var>(visitor_math<op_plus>{}, p1, p2);
  }
  static t_var minus(const t_var & p1, const t_var & p2) {
    return std::visit<t_var>(visitor_math<op_minus>{}, p1, p2);
  }
  static t_var mult(const t_var & p1, const t_var & p2) {
    return std::visit<t_var>(visitor_math<op_mult>{}, p1, p2);
  }
  static t_var div(const t_var & p1, const t_var & p2) {
    return std::visit<t_var>(visitor_math<op_div>{}, p1, p2);
  }
  static t_var mod(const t_var & p1, const t_var & p2) {
    return std::visit<t_var>(visitor_math<op_mod>{}, p1, p2);
  }

  struct visitor_print {
    void operator () (t_mono) { std::cout << "null"; }
    void operator () (t_int p) { std::cout << p; }
    void operator () (t_float p) { std::cout << p; }
  };

  static void print(const t_var & p) {
    std::visit<void>(visitor_print{}, p);
  }
};

int main() {
  using t_safe = safe<std::intmax_t, long double>;
  using t_var = t_safe::t_var;
  t_var v1 = t_safe::s_int_min, v2 = -1, v3 = t_safe::t_mono{};
  t_var v_res = t_safe::mod(v1, v2);
  std::cout << std::setprecision(t_safe::t_limits_float::digits10);
  t_safe::print(v_res);
  std::cout << '\n';
  v_res = t_safe::plus(v1, v3);
  t_safe::print(v_res);
  std::cout << '\n';
  return 0;
}


Но в моём реальном проекте код немного другой.