Проверка на выход за пределы int

Настасья хотела спросить : https://ru.stackoverflow.com/questions/1280905/Проверка-на-выход-за-пределы-int/1280957#1280957

Я перевожу string в int через std::atoi(), однако строка может превышать диапазон значений int (внутри string может быть больше 2 147 483 647)

Думала проверять по количеству символов, если больше 7, чтобы выдавать ошибку и далее проверять посимвольно строку на максимальное число. Проблема возникает, если я выполняю арифметические операции, происходит выход за пределы. Как правильно проверить выходит ли число за пределы своего значения?

Я пытался ответить так :

Есть такое понятие как двухфакторная аутентификация. В нашем случае прочитайте число из строки, переведите в новую строку и сравните.

# include <string>
# include <sstream>
# include <iostream>
bool isBADstrtoint(std::string const & st,int & in) {
    int i = std::atoi(st.c_str());
    std::stringstream ss;
    ss << i;
std:: cout<<"ss.str = '"<<ss.str()<<"'"<<std::endl;
    if (st == ss.str()) {
        in = i ;
        return false ; }
    return true ; }

int main(){
    int i;
    std::string st = "12345678901";
    if( isBADstrtoint(st,i))
      std::cout<<"st = '"<<st <<"' is bad"<<std::endl;  
    else
      std::cout<<"i = "<<i <<std::endl;
}

Работает только с целыми числами, экспонету не принимает.

ss.str = '-539222987'
st = '12345678901' is bad

При работе с арифметическими операциями вы должны уже сразу сами проверять переполнение.

int x, y , s ;
s = x + y ;
if ( y > 0 )
  if ( s > x )
    Ok
  else 
    Bad
else
  if ( y < 0 )
    if ( s < x )
      Ok
    else
      Bad

Но мой ответ был негативно оценён. Сказали что при строках, которые не могут представлять числа этого типа, компилятор может возвращать любое число и дальнейшее выполнение никак не гарантирует ход выполнения кода программы, которое использует это возвращаемое значение.

То-же самое со сложением чисел. Если происходит переполнение, то компилятор не гарантирует адекватный ответ при сравнении равенства == или <.

Вот вам реальный пример, когда при наступлении UB тип int ведёт себя так, словно диапазон представимых им значений больше, чем есть на самом деле. – wololo

#include <iostream>
#include <limits>
#include <type_traits>
using std::cout;
using std::endl;

int main()
{
  volatile int tmp = std::numeric_limits<int>::max();
  
  int a = tmp;
  
  cout << "a+1 > a: " << ( int(a+1) > a ) << endl;
  
  cout << "a+1:     " << int(a+1)      << endl;
  cout << "a:       " << a             << endl;
}

g++ -std=c++2a -O3 -Wall -pedantic -pthread main.cpp && ./a.out
a+1 > a: 1
a+1:     -2147483648
a:       2147483647

Как проверять, что число в строке может быть предоставлено типом int правильно?


Ответы (2 шт):

Автор решения: user7860670

Вот пример использования проверенных библиотечных реализаций:

  1. Использование boost.lexical cast для преобразования строки:
#include <boost/lexical_cast.hpp>

#include <iostream>

int main()
{
    try
    {
        auto const value{::boost::lexical_cast<int>("33423049234")};
        ::std::cout << "good " << value;
    }
    catch (::boost::bad_lexical_cast const & exception)
    {
        ::std::cout << "bad";
    }
    return 0;
}

https://godbolt.org/z/jqP7szqYf

  1. Использование boost.safe numerics для контроля арифметических операций:
#include <boost/safe_numerics/safe_integer.hpp>

#include <iostream>
#include <limits>
#include <system_error>

int main()
{
    ::boost::safe_numerics::safe<int> const x{::std::numeric_limits<int>::max()};
    ::boost::safe_numerics::safe<int> const y{1};
    try
    {
        auto const result{x + y};
        ::std::cout << "good " << static_cast<int>(result);
    }
    catch (::std::system_error const & exception)
    {
        ::std::cout << "bad " << exception.code();
    }
    return 0;
}

https://godbolt.org/z/Wsb5Gsjvz

→ Ссылка
Автор решения: Pak Uula

Ответ очень сильно зависит от того, что "Настасья" понимает под "правильно проверить выходит ли число за пределы своего значения".

Например, использование расширения компилятора - это "правильно"? Если да, то в GCC и Clang есть примитивы для целочисленной арифметики с переполнением.

"Правильно" ли использовать специальный API платформы? Например, Windows intsafe.h?

Входит ли в понятие "правильно" использование сторонних библиотек? Например, safe-math.h

Можно ли "правильно" писать на ассемблере? Сделать свою реализацию функций сложения/вычитания/умножения для целевой платформы, которая проверяет Overflow флаг.

Если же под "правильно" понимается строгое следование стандарту, то в стандарте переполнение есть undefined behavior. Другими словами, нужны дополнительные проверки для результата и операндов:

  1. результат сложения положительных чисел больше любого слагаемого
  2. результат сложения отрицательных чисел меньше любого слагаемого
  3. при произведении положительных целых чисел определить позицию старшего бита в каждом из множителей и убедиться, что сумма этих позиций строго меньше размера типа в битах.

Как-то так.

UPDATE

Как справедливо заметил @gbg, после того, как переполнение случилось, проверять уже поздно, ибо варианты неопределённого поведения могут быть разные. Например, включение системы самоуничтожения, как на ракете Ariane 5

Поэтому проверять надо заранее. Например, при сложении, если a > 0 то b <= (MAX_INT - a)

→ Ссылка