Преобразование беззнакового целого числа в строку и обратно. Первый опыт.

На си начал писать недавно. Со стандартными библиотеками еще не сталкивался. Вдобавок постоянно видел в сети нарекания на эти библиотеки в плане ресурсоемкости. Так что решил отложить стандартные библиотеки до лучших времен.
Сейчас выполняю один проект. И столкнулся с преобразованием чисел в строку и обратно. Сначала я наткнулся на один сайт, взял оттуда пример. Этот пример мне не совсем понравился, но работает. Потом зашел на один ирк-канал. Тамошние участники меня убеждали использовать стандартные либы и ссылку подкинули. И как я на нее не наткнулся? Мне подошел способ со степенями 10. Тем более, что сам это делал на асме. Код поправил под себя. Выкладываю:

//========================================================================
char num_str_buf [11];
//========================================================================

//========================================================================
void clr_num_str_buf (void)
{
   char *ptr = num_str_buf;
   u08 cnt = 10;

   while (cnt)
   {
      *ptr++ = ' ';
      cnt--;
   }
   *ptr = 0;
}
//========================================================================

//========================================================================
u16 __flash tab_10_16 [] =
{
   10000U,
   1000U,
   100U,
   10U,
};
//------------------------------------------------------------------------
void int_to_str (u16 value)
{
   clr_num_str_buf ();

   char *ptr = num_str_buf;

   u16 a;

   u08 cnt_1;
   u08 cnt_2;

   u16 __flash *ptr_f = tab_10_16;

   cnt_1 = 4;

   while (cnt_1)
   {
      a = *ptr_f;
      cnt_2 = 0;

      while (value >= a)
      {
         value -= a;
         cnt_2++;
      }

      if (!(flags & (1<<0)))
      {
         if (cnt_2 == 0)
         {
            *ptr++ = ' '; // Гашение незначащих нулей.
         }
         else
         {
            *ptr++ = (cnt_2 | 0x30);
            setb (flags, 0);
         }
      }
      else
      {
         *ptr++ = (cnt_2 | 0x30);
      }

      ptr_f++;
      cnt_1--;
   }
   *ptr++ = (value | 0x30);
   *ptr = 0;
}
//========================================================================

//========================================================================
u32 __flash tab_10_32 [] =
{
   1000000000U,
   100000000U,
   10000000U,
   1000000U,
   100000U,
   10000U,
   1000U,
   100U,
   10U,
};
//------------------------------------------------------------------------
void long_to_str (u32 value)
{
   clr_num_str_buf ();

   char *ptr = num_str_buf;

   u32 a;

   u08 cnt_1;
   u08 cnt_2;

   u32 __flash *ptr_f = tab_10_32;

   cnt_1 = 9;

   while (cnt_1)
   {
      a = *ptr_f;
      cnt_2 = 0;

      while (value >=a)
      {
         value -= a;
         cnt_2++;
      }

      if (!(flags & (1<<0)))
      {
         if (cnt_2 == 0)
         {
            *ptr++ = ' '; // Гашение незначащих нулей.
         }
         else
         {
            *ptr++ = (cnt_2 | 0x30);
            setb (flags, 0);
         }
      }
      else
      {
         *ptr++ = (cnt_2 | 0x30);
      }

      ptr_f++;
      cnt_1--;
   }

   *ptr++ = (value | 0x30);
   *ptr = 0;
}
//========================================================================


Ну с преобразованием числа в строку вроде разобрался, теперь осталось строку в число. Поискал в инете, попробовал стандартные либы. Тут картина грустная, я с ними никогда не работал. И либо лажа выходила, либо если получилось, код сразу раздувался на 1,5 — 2 кб как минимум. В общем, меня пока, ну просто жаба душит, тратить столько флеша и корежит от стандартных либ. Хочется сделать более-менее компактные функции.
До этого у меня проблема с самодельными функциями была из-за использования беззнаковых чисел. Компилятор постоянно либо матерился, что мои функции не будут работать, либо ругался на то, что в данном случае нужно знаковое число. Благодаря ссылке я вроде понял, как можно преобразование чисел сделать самому.
Только что закончил преобразование строки в число. Вроде работает. Выкладываю. Адекватные, обоснованные критика и замечания приветствуются.

//========================================================================
// Я пока в раздумьях, на какой реакции остановиться, если в строке нет чисел.
// Можно сделать так: u16, u08 dec_hex_u16 (void). u08 0 - ошибка. 1 - ошибок нет.
// Также можно сделать следующее. Если разрядность строки больше u16 - ошибка.

u16 str_to_int (void)
{
   char *ptr_num = tmp_num_str_buf;
   u16 __flash *ptr_tab_10 = tab_10_16;
   u16 a;
   u08 cnt = 0;
   u16 tmp_value = 0;

   while (cnt < 4)
   {
      if ((*ptr_num != ' ') || (*ptr_num != '0'))
      {
         a = (*ptr_num - 0x30);

         while (a)
         {
            tmp_value += *ptr_tab_10;
            a--;
         }
      }
      *ptr_num++;
      ptr_tab_10++;
      cnt++;
   }
      return tmp_value += (*ptr_num - 0x30);
}
//========================================================================

//========================================================================
u32 str_to_long (void)
{
   char *ptr_num = tmp_num_str_buf;
   u32 __flash *ptr_tab_10 = tab_10_32;
   u32 a;
   u08 cnt = 0;
   u32 tmp_value = 0;

   while (cnt < 9)
   {
      if ((*ptr_num != ' ') || (*ptr_num != '0'))
      {
         a = (*ptr_num - 0x30);

         while (a)
         {
            tmp_value += *ptr_tab_10;
            a--;
         }
      }
      *ptr_num++;
      ptr_tab_10++;
      cnt++;
   }
      return tmp_value += (*ptr_num - 0x30);
}
//========================================================================


Автору — neiver статьи по ссылке благодарность. Только в этой статье нашел то, что нужно.

Комментарии (13)

RSS свернуть / развернуть
эх. давненько не было…
«КАТ!»(с)
0
  • avatar
  • xar
  • 16 марта 2014, 00:47
Кажись эта тема здесь бывала — 1 и 2
0
Многие прошли через «КАТ!»(с)…
0
Ритуал посвящения=))
0
Раз уж тема зашла. Я тут нашёл самый крутой вариант реализации int2str. Мой личный хитпарад выиграли программеры из ST, писавшие примеры для Дискавери. Пока что это самый индусский код, который мне попадался лично (ну я не так часто смотрю чужой код, правда).
STM32L1_Discovery_Firmware_Pack_V1.0.3\Projects\AN3413-Current_consumption_touch_sensing\src\discover_functions.c
void convert_into_char(uint32_t number, uint16_t *p_tab)
{
  uint16_t units=0, tens=0, hundreds=0, thousands=0, misc=0;
  
  units = (((number%10000)%1000)%100)%10;
  tens = ((((number-units)/10)%1000)%100)%10;
  hundreds = (((number-tens-units)/100))%100%10;
  thousands = ((number-hundreds-tens-units)/1000)%10;
  misc = ((number-thousands-hundreds-tens-units)/10000);
  
  *(p_tab+4) = units + 0x30;
  *(p_tab+3) = tens + 0x30;
  *(p_tab+2) = hundreds + 0x30;
  *(p_tab+1) = thousands + 0x30;
  *(p_tab) = misc + 0x30;

}

Ну про "(((number%10000)%1000)%100)%10" я вообще молчу. Но вот как можно было написать такое:
misc = ((number-thousands-hundreds-tens-units)/10000);

я отказываюсь понимать! Я минут пять был в ступоре и не мог понять, почему это все-таки работает, хотя на мой взгляд не должно! Раньше не понимал людей, которые предпочитают регистры, а не StdPerifLib. По мне даже такая неудобная библиотека лучше кучи регистров, которые легко перепутать. Но теперь уже сомневаюсь, если библиотеку писали такие программеры, то добавляя её в свой софт, можно ему всю карму прошивке попортить.
0
  • avatar
  • ACE
  • 16 марта 2014, 02:34
Какая красота… прям как сказка(ужастик) на ночь для програмиста!
0
как вариант интерпретации примера из Кернигана Ричи, в какой системе переводить указывается в dev (2,10,16):
void Int_to_str(uint32_t in,uint8_t *out,uint8_t dev)
{
uint32_t temp;
uint8_t loop;
if (!in)
{
	out[0]='0';
	out[1]=0;
	return;
}
for (temp=in,loop=0;temp;temp/=dev,loop++);
loop--;
out[loop]=0;
for(;loop<0x7F;loop--,in/=dev) out[loop]="0123456789ABCDEF"[in%dev];
}
0
кстати это вариант для архитектур с аппаратным делителем
0
Я брал пример из другого учебника. Работает.
/* itoa:  конвертируем n в символы в s */
void itoa (int n, char s[])
{
	 int i, sign;

	 if ((sign = n) < 0)  			/* записываем знак */
		 n = -n;          			/* делаем n положительным числом */
	 i = 0;
	 do 
	 {       					/* генерируем цифры в обратном порядке */
		 s[i++] = n % 10 + '0';   	/* берем следующую цифру */
	 } 
	 while ((n /= 10) > 0);     	/* удаляем */
	 if (sign < 0)
		 s[i++] = '-';
	 s[i] = '\0';
	 reverse (s);
}



/* reverse:  переворачиваем строку s на месте */
void reverse (char s[])
{
	 int i, j;
	 char c;

	 for (i = 0, j = strlen(s) - 1; i < j; i++, j--) 
	 {
		 c = s[i];
		 s[i] = s[j];
		 s[j] = c;
	 }
}
0
  • avatar
  • SOVA
  • 16 марта 2014, 17:44
Взято из этой книги, если не ошибаюсь
0
Как-то не нравится мне этот кусок

if (cnt_2 == 0) *ptr++ = ' '; // Гашение незначащих нулей.


по идее он все нули в пробелы превратит.
0
А с преобразованием строки в число вообще проблем меньше всего должно быть
Упрощенно

s=0;
while(*ptr) {
//тут проверка на допустимые символы, я ее пропускаю

s = s*10 + (*ptr-0x30);
// эстеты могут умножение на десять заменить на (s<<3+s<<1)
ptr++;
}
0
А с преобразованием строки в число вообще проблем меньше всего должно быть
Упрощенно
немного ломает мозг фраза, но смысл в принципе понятен, надо еще оговориться что это преобразование подходит только для строк с десятичным представлением числа
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.