Проверка на выход за пределы 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 шт):
Вот пример использования проверенных библиотечных реализаций:
- Использование 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
- Использование 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;
}
Ответ очень сильно зависит от того, что "Настасья" понимает под "правильно проверить выходит ли число за пределы своего значения".
Например, использование расширения компилятора - это "правильно"? Если да, то в GCC и Clang есть примитивы для целочисленной арифметики с переполнением.
"Правильно" ли использовать специальный API платформы? Например, Windows intsafe.h?
Входит ли в понятие "правильно" использование сторонних библиотек? Например, safe-math.h
Можно ли "правильно" писать на ассемблере? Сделать свою реализацию функций сложения/вычитания/умножения для целевой платформы, которая проверяет Overflow флаг.
Если же под "правильно" понимается строгое следование стандарту, то в стандарте переполнение есть undefined behavior. Другими словами, нужны дополнительные проверки для результата и операндов:
- результат сложения положительных чисел больше любого слагаемого
- результат сложения отрицательных чисел меньше любого слагаемого
- при произведении положительных целых чисел определить позицию старшего бита в каждом из множителей и убедиться, что сумма этих позиций строго меньше размера типа в битах.
Как-то так.
UPDATE
Как справедливо заметил @gbg, после того, как переполнение случилось, проверять уже поздно, ибо варианты неопределённого поведения могут быть разные. Например, включение системы самоуничтожения, как на ракете Ariane 5
Поэтому проверять надо заранее. Например, при сложении, если a > 0 то b <= (MAX_INT - a)