реклама на сайте
подробности

 
 
3 страниц V   1 2 3 >  
Reply to this topicStart new topic
> Показать число с leading zeros.
Jenya7
сообщение Jan 21 2015, 07:38
Сообщение #1


Профессионал
*****

Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075



Мне нужно показать число (счетчик) с leading zeros, 8 позиций.
То есть вместо 1234 показать 00001234.
Делаю так
Код
void LCD_DisplayUpperCounter(uint32_t number)
{
    char strbuf[9] = "";
    ItoA(number, strbuf);
    char str[9] = "";

    if(number<10)
        strcat(str,"0000000");
    else if(number<100)
        strcat(str,"000000");
    else if(number<1000)
        strcat(str,"00000");
    else if(number<10000)
        strcat(str,"0000");
    else if(number<100000)
        strcat(str,"000");
    else if(number<1000000)
        strcat(str,"00");
    else if(number<10000000)
        strcat(str,"0");
    else
    {
        //str = "";
    }

    strcat(str,strbuf);
    LCD_DisplayUpperText(str);
}

Все работает. Вопрос можно ли оптимизировать функцию?

Сообщение отредактировал Jenya7 - Jan 21 2015, 07:39
Go to the top of the page
 
+Quote Post
SM
сообщение Jan 21 2015, 07:43
Сообщение #2


Гуру
******

Группа: Свой
Сообщений: 7 946
Регистрация: 25-02-05
Из: Moscow, Russia
Пользователь №: 2 881



1) sprintf(strbuf,"%08u",number); Притащит немало лишнего кода из либы.

2) заполнить второй буфер нулями, и потом туда перекопировать с конца то, что сделал itoa.

char str[9] = "00000000";

itoa(number, strbuf);
strcpy(str + 8 - strlen(strbuf), strbuf);
Go to the top of the page
 
+Quote Post
Jenya7
сообщение Jan 21 2015, 08:40
Сообщение #3


Профессионал
*****

Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075



Цитата(SM @ Jan 21 2015, 13:43) *
1) sprintf(strbuf,"%08u",number); Притащит немало лишнего кода из либы.

2) заполнить второй буфер нулями, и потом туда перекопировать с конца то, что сделал itoa.

char str[9] = "00000000";

itoa(number, strbuf);
strcpy(str + 8 - strlen(strbuf), strbuf);


спасибо. по моему это более быстрое решение. хотя тут есть вычисление офсета и длины строки. надо будет листинг посмотреть.
Go to the top of the page
 
+Quote Post
SM
сообщение Jan 21 2015, 08:45
Сообщение #4


Гуру
******

Группа: Свой
Сообщений: 7 946
Регистрация: 25-02-05
Из: Moscow, Russia
Пользователь №: 2 881



Еще, если Ваш CPU умеет быстро вычислять экспоненту числа, то можно сразу вычислить смещение, куда и делать itoa. Но, это уже вне стандартного С, тут придется работать с интринсиками, и забыть о кроссплатформенности.

Еще, думаю, самый быстрый способ, сделать свой itoa:


char str[9];
int num_table[9] = {10000000, 1000000,100000,10000,1000,100,10,1,0};
int p=0;
char *str_p = str;
int i;

while (num_table[p]) {
i=0; while ( (number-=num_table[p]) >= 0) i++;
number += num_table[p++];
*str_p++='0' + i;
}


Извиняюсь, если где ошибся, писал прямо сюда, ни откуда не копировал. И никаких itoa с собой не притащит из либы.
И, добавлю, это быстро, если в процессоре нет аппаратного деления.
Go to the top of the page
 
+Quote Post
Jenya7
сообщение Jan 21 2015, 09:11
Сообщение #5


Профессионал
*****

Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075



интересно. спасибо. надо проверить.
Go to the top of the page
 
+Quote Post
ViKo
сообщение Jan 21 2015, 09:24
Сообщение #6


Универсальный солдатик
******

Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362



Цитата(SM @ Jan 21 2015, 11:45) *
Еще, думаю, самый быстрый способ, сделать свой itoa:

Тем более, что в Keil, например, нет itoa для ARM.
Я сотворил когда-то вот такую функцию для Cortex. Но мне нужно, наоборот, чтобы лидирующие нули не писались. Можно переделать.
Код
void itoad_conv(int32_t numb, char *str)
{
  *(uint32_t *)(str)     = 0x20202020;
  *(uint32_t *)(str + 4) = 0x20202020;    
  *(uint32_t *)(str + 8) = 0x00302020;
  str += 10;
  char  sign = '+';
  if (!numb) return;
  if (numb < 0) { numb = -numb; sign = '-'; }
  do { *str-- = numb % 10 + '0'; } while (numb /= 10);
  *str = sign;
}
Go to the top of the page
 
+Quote Post
SM
сообщение Jan 21 2015, 09:41
Сообщение #7


Гуру
******

Группа: Свой
Сообщений: 7 946
Регистрация: 25-02-05
Из: Moscow, Russia
Пользователь №: 2 881



Цитата(ViKo @ Jan 21 2015, 12:24) *
numb % 10


Вот это - всегда делается за 32 итерации условного вычитания (если деление не аппаратное, да и там оно бывает итеративным), или условного сложения-вычитания (для 32-битного int). Поэтому, это не может быстрее, так как в том, что я предложил, для каждой цифры максимум 10 итераций вычитания, что в три раза меньше в самом худшем случае.

Цитата(ViKo @ Jan 21 2015, 12:24) *
Тем более, что в Keil, например, нет itoa для ARM.

Ой. А это, как? Это же ANSI стандартизованная ф-ция. Обязана быть!
Go to the top of the page
 
+Quote Post
ViKo
сообщение Jan 21 2015, 09:47
Сообщение #8


Универсальный солдатик
******

Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362



Цитата(SM @ Jan 21 2015, 12:41) *
Ой. А это, как? Это же ANSI стандартизованная ф-ция. Обязана быть!

Нема, зуб даю. Года 4 назад писал об этом здесь же. И эту же программу выкладывал.
itoa - не стандартная функция. Предлагается пользоваться sprintf().
Go to the top of the page
 
+Quote Post
SM
сообщение Jan 21 2015, 09:51
Сообщение #9


Гуру
******

Группа: Свой
Сообщений: 7 946
Регистрация: 25-02-05
Из: Moscow, Russia
Пользователь №: 2 881



Цитата(ViKo @ Jan 21 2015, 12:47) *
Нема, зуб даю.

Да, Вы правы. Внатуре это нестандартное расширение... Вот бы не подумал, был уверен в обратном.
Go to the top of the page
 
+Quote Post
Jenya7
сообщение Jan 21 2015, 10:00
Сообщение #10


Профессионал
*****

Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075



я пользуюсь этим
Код
  
void Reverse(char s[])
{
  int c, i, j;

  for (i = 0, j = strlen(s)-1; i < j; i++ , j--)
  {
    c = s[i];
    s[i] = s[j];
    s[j] = c;
  }
  //s[i] = '\0';
}

void ItoA(uint32_t n , char s[])
{
  int i = 0;
  do
  {
    s[i++] = n % 10 + '0';
  }
  while ((n /= 10) >= 1);
   //s[i] = '\0';
  Reverse(s);
}



Сообщение отредактировал Jenya7 - Jan 21 2015, 10:01
Go to the top of the page
 
+Quote Post
SM
сообщение Jan 21 2015, 10:07
Сообщение #11


Гуру
******

Группа: Свой
Сообщений: 7 946
Регистрация: 25-02-05
Из: Moscow, Russia
Пользователь №: 2 881



Извращение какое... Сначала сделать строку с направлением через зад, потом еще через зад ее развернуть, а потом еще и нулями добить... А сразу буфер нулями заполнить, и, как у ViKo, назад его заполнять, слабо? Хотя, мой вариант еще прямее, не требует заполнения нулями.
Go to the top of the page
 
+Quote Post
Jenya7
сообщение Jan 21 2015, 10:48
Сообщение #12


Профессионал
*****

Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075



Цитата(SM @ Jan 21 2015, 16:07) *
Извращение какое... Сначала сделать строку с направлением через зад, потом еще через зад ее развернуть, а потом еще и нулями добить... А сразу буфер нулями заполнить, и, как у ViKo, назад его заполнять, слабо? Хотя, мой вариант еще прямее, не требует заполнения нулями.

это общий случай. когда не требуется нули впереди добавлять.
Go to the top of the page
 
+Quote Post
andrew_b
сообщение Jan 21 2015, 11:01
Сообщение #13


Профессионал
*****

Группа: Свой
Сообщений: 1 975
Регистрация: 30-12-04
Из: Воронеж
Пользователь №: 1 757



Цитата(SM @ Jan 21 2015, 12:51) *
Да, Вы правы. Внатуре это нестандартное расширение... Вот бы не подумал, был уверен в обратном.
Перепутали с atoi.
Go to the top of the page
 
+Quote Post
CrimsonPig
сообщение Jan 21 2015, 11:37
Сообщение #14


Местный
***

Группа: Участник
Сообщений: 329
Регистрация: 23-04-14
Пользователь №: 81 502



Цитата(Jenya7 @ Jan 21 2015, 10:00) *
я пользуюсь этим
Код
  
void Reverse(char s[])
{
  int c, i, j;

  for (i = 0, j = strlen(s)-1; i < j; i++ , j--)
  {
    c = s[i];
    s[i] = s[j];
    s[j] = c;
  }
  //s[i] = '\0';
}

void ItoA(uint32_t n , char s[])
{
  int i = 0;
  do
  {
    s[i++] = n % 10 + '0';
  }
  while ((n /= 10) >= 1);
   //s[i] = '\0';
  Reverse(s);
}


Любителям прооптимизировать всякую мутоту на заметку:
1. операция взятия остатка от деления: (n%10) и деления (n/=10) насколько они дешевы ? Поддерживаются ли они на уровне железа процессора ? Можно ли без них обойтись ?
2. В цикле for (i = 0, j = strlen(s)-1; i < j; i++ , j--) есть наивное предположение, что компилятор вынесет strlen(s) за цикл как инвариант. В общем случае неверно. s передается как параметр в функцию и компилятор имеет право решить, что данные строки могут изменяться извне в другом треде, например. Поэтому с чистой совестью может оставить вызов strlen в каждой итерации цикла.
В то время как библиотечные функции (если они есть) все-таки пишутся обычно более качественно.

Go to the top of the page
 
+Quote Post
Jenya7
сообщение Jan 21 2015, 12:03
Сообщение #15


Профессионал
*****

Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075



Цитата(CrimsonPig @ Jan 21 2015, 17:37) *
Любителям прооптимизировать всякую мутоту на заметку:
1. операция взятия остатка от деления: (n%10) и деления (n/=10) насколько они дешевы ? Поддерживаются ли они на уровне железа процессора ? Можно ли без них обойтись ?
2. В цикле for (i = 0, j = strlen(s)-1; i < j; i++ , j--) есть наивное предположение, что компилятор вынесет strlen(s) за цикл как инвариант. В общем случае неверно. s передается как параметр в функцию и компилятор имеет право решить, что данные строки могут изменяться извне в другом треде, например. Поэтому с чистой совестью может оставить вызов strlen в каждой итерации цикла.
В то время как библиотечные функции (если они есть) все-таки пишутся обычно более качественно.


так нету библиотечных функций в том то и дело. и более лучшей генерик функции я не нашел.
Go to the top of the page
 
+Quote Post

3 страниц V   1 2 3 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 23rd July 2025 - 15:59
Рейтинг@Mail.ru


Страница сгенерированна за 0.015 секунд с 7
ELECTRONIX ©2004-2016