детектировать переполнения при целочисленных операциях
От: Sm0ke Россия ksi
Дата: 14.08.22 14:43
Оценка:
Есть два целых числа типа std::intmax_t
Необходимо произвести над ними одну из следующих операций: сложение, вычитание, умножение.
Но при переполнении результата преобразовать числа в double перед операцией и вернуть double.
А если переполнения нет, то вернуть целый результат.

Можно конечно сделать double операцию, и сравнить результат с диапазоном min() max() целого. Если всё ок, то произвести целочисленную операцию.

Какие есть ещё варианты?
overflow cpp int float
Re: детектировать переполнения при целочисленных операциях
От: DiPaolo Россия  
Дата: 14.08.22 14:52
Оценка: 3 (1)
S>Какие есть ещё варианты?

1) ловить исключение. Стандартная библиотека это не поддерживает, но буст может:

The mathematical functions of the standard library components do not throw this exception (mathematical functions report overflow errors as specified in math_errhandling). Third-party libraries, however, use this. For example, boost.math throws std::overflow_error if boost::math::policies::throw_on_error is enabled (the default setting).

(https://en.cppreference.com/w/cpp/error/overflow_error)

2) заморочиться с логикой на битах. Типа, если у нас сложение, и старшие биты обеих чисел нули, то можно спокойно складывать целочисленно, ну и так далее для других операций.
Патриот здравого смысла
Re: детектировать переполнения при целочисленных операциях
От: koenjihyakkei Россия  
Дата: 14.08.22 16:16
Оценка: 7 (2) +2
Здравствуйте, Sm0ke, Вы писали:

__builtin_add_overflow, __builtin_sub_overflow, __builtin_mul_overflow
Re[2]: детектировать переполнения при целочисленных операциях
От: Sm0ke Россия ksi
Дата: 15.08.22 20:25
Оценка:
Здравствуйте, koenjihyakkei, Вы писали:

K>Здравствуйте, Sm0ke, Вы писали:


K>__builtin_add_overflow, __builtin_sub_overflow, __builtin_mul_overflow


Они я так понимаю только в GCC доступны. Необходим портируемый способ. Я использую VS community 22.
Re[3]: детектировать переполнения при целочисленных операциях
От: T4r4sB Россия  
Дата: 15.08.22 20:39
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>Они я так понимаю только в GCC доступны. Необходим портируемый способ. Я использую VS community 22.


Ну тогда давай по-сложному.

Переполнение происходит тогда и только тогда, когда сумма двух неотрицательных отрицательно либо когда сумма двух отрицательных неотрицательна. Проверяешь знаки чисел до и после операции короч
Re[3]: детектировать переполнения при целочисленных операциях
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 15.08.22 21:42
Оценка:
Здравствуйте, Sm0ke, Вы писали:

K>>Здравствуйте, Sm0ke, Вы писали:


K>>__builtin_add_overflow, __builtin_sub_overflow, __builtin_mul_overflow


S>Они я так понимаю только в GCC доступны. Необходим портируемый способ. Я использую VS community 22.


Intsafe.h?

https://developercommunity.visualstudio.com/t/please-implement-integer-overflow-detection/409051

Стырить реализацию у GCC? Можно тупо им скомпилить, вытащить асм и собрать его в отдельный obj?
Хотя без call тут не получится в итоге

Можно самому написать, вроде не слишком сложно

#include <cstdlib>
#include <iostream>


bool my_sadd_overflow (int a, int b, int *res)
{
    const unsigned hb = ~(((unsigned)-1)>>1);

    #if 0
    //if (a>=0 && b>=0)
    if (!(a&hb) && !(b&hb))
    {
        *res = a+b;
        //if (*res<0) // Компилятор может подгадить оптимизацией
        return ((*res) & hb);
            return true;
    }
    //else if (a<0 && b<0)
    else if ((a&hb) && (b&hb))
    {
        *res = a+b;
        //if (*res>=0)
        if (!((*res) & (~(((unsigned)-1)>>1))))
            return true;
    }
    else // разный знак, переполнения не будет
    {
        *res = a+b;
    }
    #endif

    int axb = a^b;
    *res = a+b;

    if (axb&hb) // разный знак, переполнения не будет
    {
        // ничего не делаем        
    }
    else // возможно переполнение
    {
        // Если оба были >=0 - старший бит 0 у обоих, у результата старший бит тоже д.б. 0
        // тоже самое наоборот
        // итого - ст. бит результата д.б. равен ст. биту любого из операндов
        // если биты равны, то по XORу результат будет 0 - всё хорошо

        return (a^(*res))&hb ? true : false;
    }


    return false; 
}

int getRandomInt()
{
    return (std::rand()*std::rand()+std::rand()) - (int)((unsigned)RAND_MAX*(unsigned)RAND_MAX/2);
}

bool test(int a, int b)
{
    std::cout<<a<<"+"<<b<<" = ";
    int r1, r2;
    if (my_sadd_overflow(a,b,&r1)!=__builtin_sadd_overflow(a,b,&r2))
    {
        std::cout<<r1<<" - Failed (overflow)\n";
        return false;
    }
    else if (r1!=r2)
    {
        std::cout<<r1<<"!="<<r2<<" - Failed (wrong res)\n";
        return false;
    }
    else
    {
        std::cout<<r1<<"=="<<r2<<" - Passed\n";
        return true;
    }

}

int main()
{
    int passedCount = 0;
    for(auto i=0; i!=1000; ++i)
    {
        if (test(getRandomInt(),getRandomInt()))
            passedCount++;
    }

    std::cout<<"Total passed: "<<passedCount<<"\n";
}


clang -std=c++1z -stdlib=libc++ -lstdc++ -O2 -Wall -pedantic -pthread main.cpp && ./a.out
http://coliru.stacked-crooked.com/a/269d9700b7976f56

https://gcc.godbolt.org/z/cbEeMb5jW

Последний вариант покороче получился. Но с интрисиками конечно не сравнить

Можно еще тут поковырять — https://docs.microsoft.com/en-us/cpp/intrinsics/compiler-intrinsics?view=msvc-170
Маньяк Робокряк колесит по городу
Re: детектировать переполнения при целочисленных операциях
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 16.08.22 05:59
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>Какие есть ещё варианты?

Это тестовое задание? Я полагаю, тут надо какой-то алгоритмический трюк использовать, возможно с битами, чтобы понять будет ли переполнение или нет до того как выполнить операцию.
Sic luceat lux!
Re[4]: детектировать переполнения при целочисленных операциях
От: Sm0ke Россия ksi
Дата: 18.08.22 16:54
Оценка:
Здравствуйте, T4r4sB, Вы писали:

TB>Здравствуйте, Sm0ke, Вы писали:


S>>Они я так понимаю только в GCC доступны. Необходим портируемый способ. Я использую VS community 22.


TB>Ну тогда давай по-сложному.


TB>Переполнение происходит тогда и только тогда, когда сумма двух неотрицательных отрицательно либо когда сумма двух отрицательных неотрицательна. Проверяешь знаки чисел до и после операции короч


Переполнение при операциях со знаковыми целыми — это UB.
Re[5]: детектировать переполнения при целочисленных операциях
От: T4r4sB Россия  
Дата: 18.08.22 17:34
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>Переполнение при операциях со знаковыми целыми — это UB.


Ну тогда шо делать, кастануть к беззнаковым, сложить, кастануть обратно.
Re: детектировать переполнения при целочисленных операциях
От: maxkar  
Дата: 19.08.22 12:05
Оценка: 4 (1) +1
Здравствуйте, Sm0ke, Вы писали:

S>Можно конечно сделать double операцию, и сравнить результат с диапазоном min() max() целого. Если всё ок, то произвести целочисленную операцию.

А есть гарантии, что при преобразовании в double не произойдет потери точности? А то ведь intmax_t может быть и 64, и больше бит. При преобразовании в double вы потеряете последние биты и потом схлопочете переполнение.

S>Какие есть ещё варианты?

Проверять обратными операциями. Псевдокод.

if (a ^ b < 0) //разный знак
  return a + b;

if (a > 0)
  if (intmax_max - a >= b)
    return a + b;
  else 
    return ((double) a) + ((double) b);

if (intmax_min - a <= b)  
    return a + b;
  else 
    return ((double) a) + ((double) b);


Проверка знаков гарантирует, что при вычислении границ мы остаемся в рамках диапазона. Т.е. вычитаем из intmax_max только неотрицательные значения.

Для умножения все гораздо сложнее. Что-то вроде:

if (a == 0 || a == 1 || b == 0 || b == 1)
  return a * b;

if (a > 0 && b > 0)
  if (intmax_max / a >= b)
    return a * b;
  else 
    return ((double) a) * ((double) b);

if (a < 0 && b < 0)
  if (a == intmax_min || b == intmax_min || (intmax_max / (-a) < (-b)))
    return ((double) a) * ((double) b);
  else
    return a * b;

if (a > 0)
  if (intmax_min / a <= b)
    return a * b;
  else 
    return ((double) a) * ((double) b);
else // b > 0
  if (intmax_min / b <= a)
    return a * b;
  else 
    return ((double) a) * ((double) b);


Обратите внимание — intmax_min обрабатывается отдельно, для него отрицание плохо работает. Может быть, можно упростить делением положительного числа на отрицательное для проверки. Нужно смотреть спецификацию.
Re: детектировать переполнения при целочисленных операциях
От: xma  
Дата: 19.08.22 12:36
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>Есть два целых числа типа std::intmax_t

S>Необходимо произвести над ними одну из следующих операций: сложение, вычитание, умножение.
S>Но при переполнении результата преобразовать числа в double перед операцией и вернуть double.
S>А если переполнения нет, то вернуть целый результат.

S>Какие есть ещё варианты?


на асме накарячить по крайней мере для стандартных int'ов (32 bits) для x86 там можно было считать бит (после совершения операции) и по нему узнать — было ли переполнение, а также напр. при умножении результат лежит уже не в одном регистре (32-bit), а в двух ..

думаю x86-64 есть аналогичные варики для long (64 bits) ..

насчёт флагов во всяких MMX/SSE/AVX/AVX2 хз, как целочисленных возможностей последних (SSE/AVX/AVX2) — хз ..

но в любом случае, даже на x86 за счёт флага-бита переполнения можно было организовать работу с целыми числами любой разрядности — без потери точности .. (актуально для банковских приложений, переводы/счета и т.д.)

подробнее тут, "Assembler. Учебник для вузов. 2-е изд. В. И. Юров"

(для общего сведения)
[страница 165] Глава 8. Арифметические команды

(ну и собственно про сами операции с двоичными числами произвольной длины)
[страница 183] Арифметические операции над двоично-десятичными числам

P.S.:

нахрен я только в своё время голову засорял подобным ..
Отредактировано 19.08.2022 12:38 xma . Предыдущая версия . Еще …
Отредактировано 19.08.2022 12:38 xma . Предыдущая версия .
Re[5]: детектировать переполнения при целочисленных операциях
От: cures Россия cures.narod.ru
Дата: 19.08.22 17:11
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>Переполнение при операциях со знаковыми целыми — это UB.


С простыми интами — UB, а с intmax?
Re[6]: детектировать переполнения при целочисленных операциях
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 20.08.22 07:09
Оценка:
Здравствуйте, T4r4sB, Вы писали:

S>>Переполнение при операциях со знаковыми целыми — это UB.


TB>Ну тогда шо делать, кастануть к беззнаковым, сложить, кастануть обратно.


"Кастануть обратно" в общем случае разве не проблемно?
(в стандарте как-то сложно искать)
(и да, C++20 не всегда есть)
The God is real, unless declared integer.
Re: примерный результат (переполнения целочисленных)
От: Sm0ke Россия ksi
Дата: 23.08.22 08:24
Оценка: 21 (2)
Всем спасибо за ответы. У меня получился примерно такой код:
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;
}


Но в моём реальном проекте код немного другой.
Отредактировано 23.08.2022 8:30 Sm0ke . Предыдущая версия . Еще …
Отредактировано 23.08.2022 8:25 Sm0ke . Предыдущая версия .
Re: детектировать переполнения при целочисленных операциях
От: Lonely Dog Россия  
Дата: 25.08.22 09:27
Оценка: -1
Здравствуйте, Sm0ke, Вы писали:

S>Есть два целых числа типа std::intmax_t

S>Необходимо произвести над ними одну из следующих операций: сложение, вычитание, умножение.
S>Но при переполнении результата преобразовать числа в double перед операцией и вернуть double.
S>А если переполнения нет, то вернуть целый результат.

S>Можно конечно сделать double операцию, и сравнить результат с диапазоном min() max() целого. Если всё ок, то произвести целочисленную операцию.


S>Какие есть ещё варианты?

В solidity вот так делают (см https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeMath.sol)
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }


Может и вам подойдет
Re[5]: детектировать переполнения при целочисленных операциях
От: reversecode google
Дата: 03.09.22 20:56
Оценка: :))
S>Переполнение при операциях со знаковыми целыми — это UB.

с каких это пор, а не наоборот ?
знаковые врапаются
Re[6]: детектировать переполнения при целочисленных операциях
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 03.09.22 21:47
Оценка: :)
Здравствуйте, reversecode, Вы писали:

S>>Переполнение при операциях со знаковыми целыми — это UB.


R>с каких это пор, а не наоборот ?

R>знаковые врапаются

Лолшто? Читай стандарт, что, так сложно, что ли?
Маньяк Робокряк колесит по городу
Re[6]: детектировать переполнения при целочисленных операциях
От: tramdol  
Дата: 14.10.22 17:33
Оценка:
Здравствуйте, cures, Вы писали:

C>Здравствуйте, Sm0ke, Вы писали:


S>>Переполнение при операциях со знаковыми целыми — это UB.


C>С простыми интами — UB, а с intmax?

если intmax знаковый то тоже
https://happydapps.com/ru/article/cpp-signed-overflow
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.