LINUX.ORG.RU

Чем парсить целые числа?

 , ,


0

3

Ситуация такова, что даже хваленый boost::lexical_cast может удивить: https://wandbox.org/permlink/PP0RTfmifVO816U0

std::cout << boost::lexical_cast<unsigned int>("-1"); // исключения не будет

Спойлер: http://www.boost.org/doc/libs/1_60_0/doc/html/boost_lexical_cast/frequently_a...

Скажите, есть ли вообще в природе средства для парсинга целых чисел (пускай даже ограниченные до radix=10) которые не удивляют?

Т.е. :

  • Не пропускают пробелов вначале строки
  • Интерпретируют всю строчку (а не до первого непонятного символа)
  • Нет автоматического определения radix (т.е. "012" будет 12 а не 10)
  • Могут интерпретировать подстроку (но польностью). Как boost::lexical_cast(str, len)
  • Трактуют 8-ми битные типы как числа (а не как символ/байт, ибо первый байт строки я и сам могу взять)
  • ... не имеют других подводных камней
★★★★★

Последнее исправление: KennyMinigun (всего исправлений: 1)
Ответ на: комментарий от lovesan

goto в перемешку с stl ітераторами? ты серьёзно?

anonymous
()
Ответ на: комментарий от dzidzitop

Ты прежде чем жечь задницы хипстеров разобрался бы что и как. Такое:

float pow(float, float);
float pow(doble, double);
float pow(int, int);
в расте не возможно, потому что нет перегрузки имен. А такое:
fn pow<T, U>(a: T, b: U) -> T;

let x = pow(FromStr::from_str("-2").unwrap(), FromStr::from_str("2").unwrap());
При компиляции выдаст ошибку что недостаточно информации для вывода типов. И предложит или сделать так:
let x = pow::<f32, i32>(FromStr::from_str("-2").unwrap(), FromStr::from_str("2").unwrap());
или так
let a: f32 = FromStr::from_str("-2").unwrap();
let b: i32 = FromStr::from_str("2").unwrap();
let x = pow(a, b);
В обоих случаях тип явно виден. А если pow не generic функция, то ее типы будут четко прописаны в документации.

pftBest ★★★★
()

Не читал тред, но осуждаювыскажусь:

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

Иначе все языки давным-давно превратились бы в базар. Всё в твоих руках. Дерзай. Парсить десятичные цифры в c/c++ очень просто: c -'0'

anonymous
()
Ответ на: комментарий от anonymous

Да, в сему мне надо больше валидратор & парсер. Т.е. любой (из «тысячи их») парсер целых стандартной библиотеки правильно обработает идеальную строку (скажем «123»). Так вот, *все* стандартные парсеры местами начинают мудрить вместо того, чтоб делать *только* то, что от них требуется. Пропускание пробелов вначале? Хорошо, но как отключить? Мне что, самому проверять? И т.д. (по критериям из топика)

KennyMinigun ★★★★★
() автор топика
Ответ на: комментарий от KennyMinigun

Стандартные библиотеки это велосипеды, которые пришлось узаконить по принципу «штобы было». Тогда числа были розовые в крапинку.

anonymous
()
Ответ на: комментарий от KennyMinigun

Мне что, самому проверять?

И кстати ты хороший вопрос задал. Хорошо поставленный вопрос отпадает сам собой.

anonymous
()
Ответ на: комментарий от Legioner

как же хорошо, что компиляторы пишут не те, у кого жёппа горит от c и c++.

было бы здорово запретить этой публике пользоваться софтом на c и c++, в первую очередь llvm. и заставить написать себе компилятор по вере своей и на языке своем

i36_zubov
()
Ответ на: комментарий от i36_zubov

Еще один считает себя умнее стандарта. Вот тебе задачка, если UB не существует, то как ты обьяснишь разницу?

int foo(int a) {
    return (a + 1) > a;
}
unsigned bar(unsigned a) {
    return (a + 1) > a;
}

foo(int):             # @foo(int)
        mov     eax, 1
        ret

bar(unsigned int):    # @bar(unsigned int)
        xor     eax, eax
        cmp     edi, -1
        setne   al
        ret
pftBest ★★★★
()
Ответ на: комментарий от pftBest

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

только вот я не пойму, на чем ты считаешь, что переполнение в int при сложении двух чисел у тебя приводит не неопределённому результату? счёты, или арифмометр феликс там.

i36_zubov
()
Ответ на: комментарий от i36_zubov

Там выше уже написали, что стандарт не определяет поведение кода в такой ситуации. Скорее всего будет implementation-defined, т.е. интерпретация результата сложения за правилами two's complement (наиболее распостраненное представление негативных чисел сейчас) и игнорирование флага переполнения.

Но компилятор имеет полное право вставить проверку на переполнение и вызвать terminate().

KennyMinigun ★★★★★
() автор топика
Ответ на: комментарий от KennyMinigun

Но компилятор имеет полное право вставить проверку на переполнение и вызвать terminate().

Вот в этом вы точно уверены?

eao197 ★★★★★
()
Ответ на: комментарий от eao197

В части «полное право» — уверен на 100%. А что компилятор сделает (т.е. про «terminate») — только пример. А к чему вопрос?

KennyMinigun ★★★★★
() автор топика
Ответ на: комментарий от KennyMinigun

В части «полное право» — уверен на 100%.

Когда утверждение начинает дробиться на части с разной степенью достоверности, то это начинает напоминать «не в лоттерею, а в карты, не выиграл, а проиграл...»

А к чему вопрос?

К тому, что на UB для переполнения знаковых чисел построено изрядное количество оптимизаций в компиляторах. Замена оптимизаций на вызов terminate с практической точки зрения выглядит нежизнеспособной идеей, имхо.

Хотя звездануть на LOR-е можно и не то еще.

eao197 ★★★★★
()
Ответ на: комментарий от i36_zubov

При чем тут неопределённый результат к UB ? Это совсем разные вещи. И какое у тебя железо и как оно считает совсем не важно, потому что ты проиграл еще на этапе компиляции, задолго до того как твой код начнет выполнять процессор. Например если ты напишешь такой код, или твой код после оптимизаций станет таким:

void foo(int a) {
    if ((a + 1) > a) {
        do_x();
        return;
    }
    do_y();
}
То в исполняемом файле даже не будет функции do_y, потому что компилятор выкинет ее нахрен вместе с проверкой. А если функции нет, то и не важно что процессор делает при переполнении.

pftBest ★★★★
()
Последнее исправление: pftBest (всего исправлений: 1)
Ответ на: комментарий от eao197

с практической точки зрения выглядит нежизнеспособной идеей, имхо.

Зависит, от того, чего у тебя требует «практическая точка зрения». В моем случае я лишь заметил, что такая возможность есть (как и возможность убить котенка) и стандарт этого не запрещает.

Но если цель была докопаться — у тебя получилось, поздравляю. Цитата тоже ничего так, я оценил.

KennyMinigun ★★★★★
() автор топика
Ответ на: комментарий от eao197

Когда утверждение начинает дробиться на части с разной степенью достоверности,

Ты не поверишь, но это написано в стандарте, цитата:

NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).

«Допустимым поведением при UB является в том числе и завершение исполнения программы с выводом диагностического сообщнения»

pftBest ★★★★
()
Ответ на: комментарий от pftBest

Растосинтаксисом не владею, поэтому использовал сиподобное нечто.

Я и говорю - разгадывать ребусы. Особенно в случае сложных выражений, и особенно в чужом коде. А то, что компилятор может это распарсить - значит нужно использовать. Нормально, чо.

Но в расте хотя бы функция есть, которая парсит строку как целое число. В могучих стандартных библиотеках Си и Си++ и такого нет - всё что-то вокруг да около.

dzidzitop ★★
()
Ответ на: комментарий от pftBest

У меня под рукой драфт C++14 от 2014-11-19 (N4296), там написано следующее:

1.3.24 [defns.undefined]
undefined behavior
behavior for which this International Standard imposes no requirements
[ Note: Undefined behavior may be expected when this International Standard omits any explicit definition of behavior or when a program uses an erroneous construct or erroneous data. Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message). Many erroneous program constructs do not engender undefined behavior; they are required to be diagnosed. —end note ]

Из этой цитаты не следует, что каждый UB может приводить к завершению программы посредством terminate. Ибо сами UB варьируются в широком диапазоне. Например, снятие const и переполнение для int-ов — это все UB. Но если первый случай попадает под «erroneous construct» и «erroneous data», то по поводу int-ов есть некоторые сомнения (ибо это скорее подпадает под «International Standard omits any explicit definition of behavior»).

Так что в LOR-овском сраче, конечно, можно прикрыться буквой стандарта и глубокомысленно заявить, что реализация имеет право дергать terminate ибо UB и все такое. Но хотелось бы видеть подтверждения таким толкованиям стандарта на практике. Есть ли (были ли) компиляторы C или C++, которые инициировали бы terminate при возникновении переполнения int-ов?

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Есть ли (были ли) компиляторы C или C++, которые инициировали бы terminate при возникновении переполнения int-ов?

clang, передаешь аргумент -fsanitize=undefined и получаешь в консоли:

main.c:3:9: runtime error: signed integer overflow: 536870912 * 8 cannot be represented in type 'int'

pftBest ★★★★
()
Ответ на: комментарий от eao197

Замена оптимизаций на вызов terminate с практической точки зрения выглядит нежизнеспособной идеей, имхо.

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

Legioner ★★★★★
()
Ответ на: комментарий от pftBest

Переставь местами. Компилятор как упаковщик - работает в контексте.

anonymous
()

Пользуйтесь!

template<bool unsign>
int parse(const string& str)
{
int ret = 0;
char c;
bool negative = false;
for(int i = 0; i<str.size(); i++)
{ 
 c = str[i];
 if(unsign && i == 0 && c == '-')
 { negative = true;
  continue;
 } 
 if(c > '9' || c < '0' ) throw "ваш любимый экзепшн";
 ret*=10;
 ret+=c-'0';
}
if(negative) ret*=-1;
return ret;
}
next_time ★★★★★
()
Последнее исправление: next_time (всего исправлений: 1)
Ответ на: Пользуйтесь! от next_time

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

next_time ★★★★★
()
Ответ на: комментарий от KennyMinigun

у вас фантастические представления о компиляторах. компилятор делает то, что ему приказано. с -O0 никакого UB не возникает. с -O1 и выше компилятор делает оптимизации: простую арифметику вида «сложить-умножить-поделить» и переносит перед этим все операции вправо от знака сравнения. страшно подумать, что было бы, если бы компиляторы писали пострадавшие от С и С++.

а вот как делает нормальный человек, не курильщик:

int foo(int a) {
    int tmp = a+1;
    asm volatile("":"=g"(tmp):"0"(tmp):"memory");
    return tmp > a;
}

foo:
        .seh_endprologue
        leal    1(%rcx), %eax
        cmpl    %eax, %ecx
        setl    %al
        movzbl  %al, %eax
        ret
нужно просто знать. на чем компилятор прерывает оптимизации. а это например asm влияющий на memory. и всё, умности кончились.

i36_zubov
()
Ответ на: комментарий от Legioner

В Rust checked arithmetic overflow в дебаге по умолчанию. И это, в том числе, делает дебаг сборки намного медленнее. В отличии от C++, где дебаг, зачастую, мало влияет на производительность (в том числе из-за того, что у раста зависимости тоже в дебаге собираются).

RazrFalcon ★★★★★
()
Ответ на: комментарий от pftBest

Зачем результат сравнения возвращать как int/unsigned?

anonymous
()
Ответ на: комментарий от i36_zubov

кстати, memory можно снять, оставив только вход переменной и выход в том же месте. в злых случаях можно «cc» поставить в clobberы.

i36_zubov
()
Ответ на: комментарий от i36_zubov

с -O0 никакого UB не возникает

твой пример как раз доказывает наличие UB. Результат выражения меняется в зависимости от параметров оптимизации, и это не бага в компиляторе.

anonymous
()
Ответ на: Пользуйтесь! от next_time

Сделать code review, что ли :-)

template<bool unsign>
//..
if (unsign && ..)
Немного не идейное использование шаблона. Т.е. нет разницы что unsign передается как аргумент вызова или как аргумент шаблона. В С++17 завезли if constexpr, что бы подошло в данном случае.

int i = 0; i<str.size()

-Wparanoic: сравнение знакового и беззнакового. Если str.size() будет больше std::numeric_limits<int>::max() (маловероятный сценарий, но все же) то будет бесконечный цикл (а точнее — UB, переполнение знакового). Слежует использовать тот же самый тип, что возвращает .size(), т.е. std::string::size_type, однако писать долго, по этому std::size_t для непрерывных контейнеров (std::string, std::vector, ...) будет нормально.

const string& str

Не стоит делать using namespace ...;. Общепринятая практика: https://google.github.io/styleguide/cppguide.html#Namespaces

for (...)
  //...
  if(unsign && i == 0 && c == '-')

Проверка в цикле того, что может случиться только один раз. Следует вынести за цикл. В GCC 7.1 появилась опция -fsplit-loops, так что есть шанс что компилятор соптимизирует.

char c;

Обьявление переменной лучше делать как можно ближе к месту ее первого ииспользования. Т.е. так:char c = str[i];.

ret*=10;
ret+=c-'0';

Переполнение не обрабатывается, аж два раза. C/C++ не предоставляет стандартного способа проверки на переполнение, по этому можно прибегнуть или к compiler-specific или поиграться с битами самому (предполагая, что у нас two's complement).

KennyMinigun ★★★★★
() автор топика
Ответ на: комментарий от KennyMinigun

вместо мастурбации на шаблоны уже написал бы парсер. Ты точно хочешь решить задачу или тебе нефиг делать?

anonymous
()

Мой вариант, правда только int32_t, но мне хватает

int parse_int32(const char* str, size_t len, int32_t* out_val)
{
    if (len == 0)
        return -1;

    int sign = 1;

    if (*str == '-')
    {
        str++;
        len--;
        sign = -1;

        if (len == 0)
            return -1;
    }

    if (len > 10)
        return -1;

    int64_t result = 0;

    for (size_t i = 0; i < len; (i++, str++))
    {
        if (*str > '9' || *str < '0')
            return -1;

        result = 10 * result + (*str - '0');
    }

    result *= sign;

    if (result > INT32_MAX || result < INT32_MIN)
        return -1;

    *out_val = (int32_t)result;
    return 0;
}
anonymous
()
Ответ на: комментарий от KennyMinigun

по этому std::size_t для непрерывных контейнеров (std::string, std::vector, ...) будет нормально.

Почему это будет ненормально для «прерывных» контейнеров?

DarkEld3r ★★★★★
()
Ответ на: комментарий от KennyMinigun

C/C++ не предоставляет стандартного способа проверки

О как, чего только не узнаешь.

А это ничего, что при увеличении числа на !=0 значение, переполнение детектируется тупым сравнением, что результат не стал больше? Да, это тормознее, чем ассемблерная проверка бита переполнения, но в тех языках, где есть «стандартный способ» трудно сказать, что оно быстрее и удобнее.

vodz ★★★★★
()
Ответ на: комментарий от vodz

По вашей ссылке вообще mov eax, 1, а не add.

Правильно, это именно то что я хочу сказать. Нельзя проверить int на переполнение «тупым сравнением, что результат не стал больше», потому что компилятор обязательно удалит такую проверку.

pftBest ★★★★
()
Ответ на: комментарий от pftBest

Чего у вас там правильного? У вас не добавление 1, а присвоение. То есть компилятор увидел, что в «a» у вас 0. А теперь, внимание, если я сравниваю с MAX_INT до и после, если «a» неизвестно, то куда оно прооптимизирует?

vodz ★★★★★
()
Ответ на: комментарий от vodz

Чего у вас там правильного? У вас не добавление 1, а присвоение. То есть компилятор увидел, что в «a» у вас 0.

Вам надо бы освежить свои знания по x86 ассемблеру прежде чем делать такие бредовые заявления. Поясню что тут происходит:

mov     eax, 1
        ret
В eax ложится возвращаемое значение, а 1 это true. Так что этот код на с выглядит так:
int foo() {
    return true;
}
Вот ссылка в доказательство если не верите мне наслово: https://godbolt.org/g/A7OuiS

Получается что компилятор заменил выражение (a + 1) > a на true тоесть удалил проверку на переполнение. И +1 тоже удалил. А вот для unsigned он это не сделал потому что не имеет права.

https://godbolt.org/g/TgX4ip

pftBest ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.