1. Си / Говнокод #26906

    +1

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    /// Checks if the token is number or not
    bool is_number(char* test_val)
    {
        const char* ROW = "0123456789\0";
        
        for (int i = 0; i < strlen(test_val); i++) {
            for (int j = 0; j < strlen(ROW); j++) {
                if (test_val[i] == ROW[j]) {
                    goto next;
                }
            }
            return false;
            next:
        }
        return true;
    }

    Попытка проверить строку на число в Си.

    Запостил: GDMaster, 02 Сентября 2020

    Комментарии (70) RSS

    • Переписал на modern C++20

      int isnum(const char* token) {
          for (char* c = token; *c != '\0'; c++) {
              if (!((*c >= 30) && (*c <= 39))) 
                  return 0;
              }
          }
          return 1;
      }
      Ответить
      • const char* же. Да и де-морганом можно лишние скобки и отрицания убрать.
        Ответить
        • Наоборот, если раскрыть, то будет два отрицания и дезъюнкция, а так только одно отрицание и конъюнкция... Де-Морган тут может заметно уменьшить пирфоманс

          Можно было бы по карте Карно попытаться сократить, но вряд ли что-то из этого выйдет
          Ответить
          • Дык отрицания в сравнения уйдут:
            if ((c < 30) || (c > 39))
            Ответить
      • максимум модерн толкинг какой-то, но не с++20
        даже на сишке это может выглядеть лучше
        Ответить
        • переписал на too old c/c++
          мамкин хакир может while переписать на for
          а борманд и 3.1415 могут поделить строку на uint64_t и въебать на ксорах, битмасках и лсд спец олимпиадное решение без сравнений
          а программист должен взять std::isdigit(std::locale), потому что в японском цифры бывают вообще не те!

          #include <iostream>
          
          bool is_number(char const * s) {
          	if (!s || !*s) 
          		return false;	// нуллптр и пустая строка не число нихуя!
          	
          	while (*s >= '0' && *s <= '9')
          		++s;
          		
          	return !*s;
          }
          
          int main() {
          	std::cout << std::boolalpha
          		<< is_number("625462345") << std::endl
          		<< is_number("625f62345") << std::endl
          		<< is_number("") << std::endl
          		<< is_number("000000000000000") << std::endl
          		<< is_number(NULL) << std::endl;
          	return 0;
          }


          https://ideone.com/3tyvcw
          Ответить
          • -1
            Ответить
          • Перевёл на «C++», добавил поддержку «минуса».
            #include <iostream>
            #include <charconv>
            #include <cstdlib>
            #include <cstring>
            
            bool is_number(const char *str)
            {
                if (!str) {
                    return false;
                }
            
                const char *endptr = str + std::strlen(str);
                long long parsed = 0;
                auto ec = std::from_chars(str, endptr, parsed);
                return (ec.ec == std::errc() && ec.ptr == endptr);
            }
            
            int main()
            {
                std::cout << std::boolalpha
                    << is_number("625462345") << std::endl
                    << is_number("625f62345") << std::endl
                    << is_number("") << std::endl
                    << is_number("000000000000000") << std::endl
                    << is_number(NULL) << std::endl
                    << is_number("-1") << std::endl
                    << is_number("-0") << std::endl
                    << is_number("-000b") << std::endl;
                return EXIT_SUCCESS;
            }

            https://wandbox.org/permlink/8pHFVfgWmKDwO05s

            Поддержка плюса, шестнадцатеричных чисел и локали планируется в следующих версиях.
            Ответить
            • Кстати, мне одному кажется, что код с std::алгоритмами читать сложнее, чем тупой фор?
              Ответить
              • Ну да, тупой же (фор).
                Правда, в этом случае он для задачи подходит так себе (отрицательные числа не обрабатывает, переполнения тоже), а уж когда понадобится проверять плавающих питухов — тупой фор превратится в адовую портянку.
                Ответить
                • Ну просто вот название функции std::from_chars(). Что именно from chars то? Хуйня какая-то переобобщённая как всегда.

                  ec - тоже очень говорящее поле. Почему не error_code?
                  Ответить
                  • > ec - тоже очень говорящее поле.
                    Из «Буста», быть может? Там, ЕМНИП, «ec» распространено.
                    Ответить
                    • Ну вообще вся эта хуйня с сокращениями в крестах изначально.

                      Чего стоят ate (at end) и showmanyc (stream how many characters).
                      Ответить
            • "38465209385740239845702983645082364952836405298357092834058926340958720394857029384750237645092839457029384575"
              у тебя говорит, что false
              Ответить
              • Так задумано: переполнение же.
                Ответить
                • в каждом стандарте какую-то хуйню тащат за уши
                  хорошо что я больше не настоящая крестоблядь!
                  Ответить
                  • Не, ну это полноценный парсер, как тот же stroll(). Поэтому на переполнении он возвращает ошибку. И это хорошо.
                    Ответить
                    • полноценный парсер придумал ещё степанов с std::stringstream и >> your-value в 90е
                      а также буст.лексикал-каст, который это вполне эксплойтил в дефолт шаблоне
                      а для больших фоннатов пирформанса был буст спирит, который реально пыщ пыщ здорово разбирал поток на токены

                      я и говорю - всё больше какой-то хуйни изобретают

                      самое главное - исходно никто не просил проверять, что значение влезет в какой-то дефолтный числовой тип
                      Ответить
                      • > изобретают

                        Всё новое - это хорошо забытое старое. Они же тупо старый добрый сишный strtoll в итераторы завернули, ничего нового придумать не сумели. Даже вон сишный код ошибки возвращают.
                        Ответить
              • А вообще да, по-хорошему надо переписать вот так:
                template<typename T>
                bool is_number(const char *str)
                {
                    if (!str) {
                        return false;
                    }
                
                    const char *endptr = str + std::strlen(str);
                    T parsed = {};
                    auto ec = std::from_chars(str, endptr, parsed);
                    return (ec.ec == std::errc() && ec.ptr == endptr);
                }

                Теперь можно явно задавать тип/размер проверяемого числа. Даже плавающих питухов поддерживает!
                Ответить
              • А у меня true!

                https://wandbox.org/permlink/v38nFlIvEvyM3nNN
                Ответить
      • В modern C++20 запретили функцию isdigit()?
        Ответить
      • !"#$%&
        Ответить
        • И ты на J подался?
          Ответить
          • Нет, я ещё не настолько поехал.
            Это число.
            Ответить
            • А ну да, 0x30 же, а не 30. Какой факап )))
              Ответить
              • Шта

                Ты умеешь переводить рандомные символы?
                Ответить
                • Нет, я умею юзать таблицу.
                  Ответить
                • Пробел легко запомнить: 0x20 (32). А до него идут исключительно нецензурные непечатные символы.
                  Ответить
                  • Какой пробел? Там в конце у тебя пробел?
                    Ответить
                  • Ну я ещё помню, что 0x30 (48) это нолик, 0x41 'A' и 0x61 'a'. И что большая часть мусора перед числами но после пробела.

                    З.Ы. А ну да, собачка на 0x40.
                    Ответить
                    • я тоже это помню
                      но '0' and '9' охуенно наглядно, бесплатно и эквивалентно каким-то хакирным кодам символов
                      Ответить
    • фу, еще и цикл в цикле. вот тебе O(n) чувак, да еще и в сишкином духе. Представим, что isdigit у нас нету
      int is_num(char const* str)
      {
      	for (char const* s = str; *s; s++) if (*s < 48 || *s > 57) return 0;
      	return 1;
      }
      Ответить
      • > цикл в цикле

        Обижаешь, у него там кубическая сложность из-за strlen().
        Ответить
        • почти алгоритим Шлемела (или кого там?)
          Ответить
        • Компиляторы это не оптимизируют? Или strlen(test_val) "нельзя" соптимизировать, потому что он не const?
          Ответить
          • Фиг знает, функция сложновата для оптимизации, память читает. Придётся сначала пруфануть, что в этом цикле в память никто не пишет. Но я не отрицаю, что могут оптимизнуть.
            Ответить
          • gcc оптимизнул, кстати.
            Ответить
          • А вот если внутри цикла писать в память через указатель или позвать что-то с сайдэффектом - то уже хуй. Начинает звать strlen() на каждой итерации чтобы не обосраться.
            Ответить
            • Ну это логично, на самом деле.

              А зачем здесь писать что-то в память?
              Ответить
              • Ну в этой задаче нет. Я просто о том, что не надо на эту оптимизацию надеяться. Чуть более сложный код и получишь О(n**2). Даже если банально переложить этот символ в другой буфер или в поле объекта что-то писнуть.
                Ответить
      • Проапгрейдил:
        #include <iostream>
        #include <cstdlib>
        #include <cstring>
        
        bool is_number(const char *str)
        {
            if (!str) { return false; }
            if (*str == '-') { str++; }
            if (!*str) { return false; }
            while (*str >= '0' && *str <= '9') { str++; }
            return !*str;
        }
        
        int main()
        {
            std::cout << std::boolalpha
                << is_number("625462345") << std::endl
                << is_number("625f62345") << std::endl
                << is_number("") << std::endl
                << is_number("000000000000000") << std::endl
                << is_number(NULL) << std::endl
                << is_number("-1") << std::endl
                << is_number("-0") << std::endl
                << is_number("-000b") << std::endl
                ;
            return EXIT_SUCCESS;
        }

        https://wandbox.org/permlink/OjZAbDNnsZmxeYKz

        UPD: Сделал конец более хакирным.
        Ответить
        • >Проапгрейдил
          ну вот, вместо одной строки стало четыре. Вечно вы всё усложняете...

          ладно, минус я профукал, признаю
          Ответить
          • vanished
            Ответить
          • > ладно, минус я профукал, признаю
            А ещё NULL и "". Но если игнорировать (как ТС-код и делает, кстати), то можно вот так загольфить:
            #include <iostream>
            #include <cstdlib>
            #include <cstring>
            
            int is_number(const char *str)
            {
                while (*str++ - 48u < 10) {};
                return !*--str;
            }
            
            int main()
            {
                std::cout << std::boolalpha
                    << is_number("625462345") << std::endl
                    << is_number("625f62345") << std::endl
                    // << is_number("") << std::endl
                    << is_number("000000000000000") << std::endl
                    << is_number("0000000.00000000") << std::endl
                    // << is_number(NULL) << std::endl
                    // << is_number("-1") << std::endl
                    // << is_number("-0") << std::endl
                    // << is_number("-000b") << std::endl
                    ;
                return EXIT_SUCCESS;
            }
            https://wandbox.org/permlink/lahbG8Yhe6WPk87V
            Ответить
            • хехе, да: у меня пустая строка это число. Какой берген

              Но знаешь, мой код оптимизирован на перформанса, так что маргинальными случаями можно и пренеберечь. Я в доке напишу, что NULL, минус, пробел, разделитель разрядов и пустая строка в бесплатной версии не поддерживаются
              Ответить
              • > оптимизирован на перформанса
                Кстати, вореант с вычитанием выглядит быстрее:
                is_num_MAPTbIwKA(char const*):
                        jmp     .L7
                .L3:
                        sub     eax, 48
                        cmp     al, 9
                        ja      .L5
                        add     rdi, 1
                .L7:
                        movzx   eax, BYTE PTR [rdi]
                        test    al, al
                        jne     .L3
                        mov     eax, 1
                        ret
                .L5:
                        xor     eax, eax
                        ret
                is_number_hakir(char const*):
                .L10:
                        movsx   eax, BYTE PTR [rdi]
                        add     rdi, 1
                        mov     edx, eax
                        sub     eax, 48
                        cmp     eax, 9
                        jbe     .L10
                        xor     eax, eax
                        test    dl, dl
                        sete    al
                        ret

                Сравнение в цикле только одно, джамп тоже один.
                https://gcc.godbolt.org/z/r7o3nP
                Ответить
                • показать все, что скрытоvanished
                  Ответить
                  • Кстати прикольно, что гцц сам догадался применить гостовскую оптимизацию к твоему коду.
                    Ответить
                    • Угу, а вот выкинуть на мороз проверку на 0 почему-то не догадался. Как и «Шланг», кстати.
                      Ответить
                      • Её нельзя выкинуть, иначе код вернёт 0 вместо 1. Если бряк вместо ретурна будет и последняя строчка как в твоём коде, то скорее всего выкинет, проверь.
                        Ответить
                      • З.Ы. Ну, с другой стороны, он мог бы додуматься вынести разделение нуля и других левых символов за цикл.
                        Ответить
            • > while (*str++ - 48u < 10) {};

              !"#$%&

              А, ты же не зря там u написал. Хитро.
              Ответить
        • А 1E+3 всякие?
          Ответить
          • А это уже плавающий питух, я на такое не подписывался.
            Ответить
            • Там целая наука!

              https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf
              http://kurtstephens.com/files/p372-steele.pdf
              Ответить
              • Люди пишут диссертации по форматированию десятичного представления плавающего питуха?
                Ответить
                • Это хлеб Кнут! Ну, почти

                  Кстати

                  https://www.cs.sjsu.edu/~mak/CS185C/KnuthStructuredProgrammingGoTo.pdf
                  Ответить
        • осталось всего ничего — скипать лидинг и трейлинг вайтспейсос, ну и в зависимости от локали разрешать ещё разделители тысяч, опять же поддерживать японские цифры, как в примере крестореференса про из-диггит
          Ответить
          • > скипать лидинг и трейлинг вайтспейсос
            Ну да, вот именно поэтому я за «from_chars» (хотя с хвостовыми пробелами моя версия соснёт, кажется).

            > ну и в зависимости от локали разрешать ещё разделители тысяч, опять же поддерживать японские цифры
            А вот именно поэтому я за «Python»:
            >>> int('൨൬൫')
            265
            Ответить
    • показать все, что скрытоvanished
      Ответить
    • показать все, что скрытоvanished
      Ответить
    • показать все, что скрытоvanished
      Ответить
    • показать все, что скрытоvanished
      Ответить
    • показать все, что скрытоvanished
      Ответить

    Добавить комментарий