Няшная / Говнокод #26906 Ссылка на оригинал

0

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  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 GDMaster, (Updated )

Комментарии (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* же. Да и де-морганом можно лишние скобки и отрицания убрать.
      Ответить
      • Наоборот, если раскрыть, то будет два отрицания и дезъюнкция, а так только одно отрицание и конъюнкция... Де-Морган тут может заметно уменьшить пирфоманс

        Можно было бы по карте Карно попытаться сократить, но вряд ли что-то из этого выйдет
        Ответить
    • максимум модерн толкинг какой-то, но не с++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
        Ответить
        • Перевёл на «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);
              }

              Теперь можно явно задавать тип/размер проверяемого числа. Даже плавающих питухов поддерживает!
              Ответить
              • Пробел легко запомнить: 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?
        Ответить
        • Фиг знает, функция сложновата для оптимизации, память читает. Придётся сначала пруфануть, что в этом цикле в память никто не пишет. Но я не отрицаю, что могут оптимизнуть.
          Ответить
        • А вот если внутри цикла писать в память через указатель или позвать что-то с сайдэффектом - то уже хуй. Начинает звать 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: Сделал конец более хакирным.
      Ответить
      • >Проапгрейдил
        ну вот, вместо одной строки стало четыре. Вечно вы всё усложняете...

        ладно, минус я профукал, признаю
        Ответить
        • > ладно, минус я профукал, признаю
          А ещё 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
              Ответить
              • надо бенчить, так как я не знаю стоимость каждой инструкции, но выглядит так, что ты прав

                эстетически вариант с минусом приятнее
                Ответить
                • Кстати прикольно, что гцц сам догадался применить гостовскую оптимизацию к твоему коду.
                  Ответить
                  • Угу, а вот выкинуть на мороз проверку на 0 почему-то не догадался. Как и «Шланг», кстати.
                    Ответить
                    • Её нельзя выкинуть, иначе код вернёт 0 вместо 1. Если бряк вместо ретурна будет и последняя строчка как в твоём коде, то скорее всего выкинет, проверь.
                      Ответить
                    • З.Ы. Ну, с другой стороны, он мог бы додуматься вынести разделение нуля и других левых символов за цикл.
                      Ответить
                      • если даже я не додумался..)

                        тот неловкий момент, когда компилятор более лучший программист, чем я
                        Ответить
                        • Именно поэтому, чтобы не чувствовать себя ниже компилятора, я за «PHP».
                          Ответить
                          • ну да, приятнее чувствовать себя тупее интерпретатора
                            Ответить
          • > while (*str++ - 48u < 10) {};

            !"#$%&

            А, ты же не зря там u написал. Хитро.
            Ответить
      • осталось всего ничего — скипать лидинг и трейлинг вайтспейсос, ну и в зависимости от локали разрешать ещё разделители тысяч, опять же поддерживать японские цифры, как в примере крестореференса про из-диггит
        Ответить
        • > скипать лидинг и трейлинг вайтспейсос
          Ну да, вот именно поэтому я за «from_chars» (хотя с хвостовыми пробелами моя версия соснёт, кажется).

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

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

Переведи на "PHP", guest!

    А не использовать ли нам bbcode?


    8