Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Показать число с leading zeros.
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
Jenya7
Мне нужно показать число (счетчик) с 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);
}

Все работает. Вопрос можно ли оптимизировать функцию?
SM
1) sprintf(strbuf,"%08u",number); Притащит немало лишнего кода из либы.

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

char str[9] = "00000000";

itoa(number, strbuf);
strcpy(str + 8 - strlen(strbuf), strbuf);
Jenya7
Цитата(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);


спасибо. по моему это более быстрое решение. хотя тут есть вычисление офсета и длины строки. надо будет листинг посмотреть.
SM
Еще, если Ваш 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 с собой не притащит из либы.
И, добавлю, это быстро, если в процессоре нет аппаратного деления.
Jenya7
интересно. спасибо. надо проверить.
ViKo
Цитата(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;
}
SM
Цитата(ViKo @ Jan 21 2015, 12:24) *
numb % 10


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

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

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

Нема, зуб даю. Года 4 назад писал об этом здесь же. И эту же программу выкладывал.
itoa - не стандартная функция. Предлагается пользоваться sprintf().
SM
Цитата(ViKo @ Jan 21 2015, 12:47) *
Нема, зуб даю.

Да, Вы правы. Внатуре это нестандартное расширение... Вот бы не подумал, был уверен в обратном.
Jenya7
я пользуюсь этим
Код
  
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);
}

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

это общий случай. когда не требуется нули впереди добавлять.
andrew_b
Цитата(SM @ Jan 21 2015, 12:51) *
Да, Вы правы. Внатуре это нестандартное расширение... Вот бы не подумал, был уверен в обратном.
Перепутали с atoi.
CrimsonPig
Цитата(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 в каждой итерации цикла.
В то время как библиотечные функции (если они есть) все-таки пишутся обычно более качественно.

Jenya7
Цитата(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 в каждой итерации цикла.
В то время как библиотечные функции (если они есть) все-таки пишутся обычно более качественно.


так нету библиотечных функций в том то и дело. и более лучшей генерик функции я не нашел.
CrimsonPig
Цитата(Jenya7 @ Jan 21 2015, 12:03) *
так нету библиотечных функций в том то и дело. и более лучшей генерик функции я не нашел.


Зато есть такая штука, называется интернет, а в ней есть гугль. А если в гугле ввети запрос "c itoa implementation", то он вывалит кучу ссылок. а примерно третьей ссылкой можно воспользоваться,
чтобы оценить масштабы бедствий:
http://www.jb.man.ac.uk/~slowe/cpp/itoa.html
SM
Цитата(Jenya7 @ Jan 21 2015, 15:03) *
так нету библиотечных функций в том то и дело. и более лучшей генерик функции я не нашел.


То, что я дал код, переделывается на работу без нулей введением одного флага, который сначала запрещен, и разрешается при первом ненулевом символе. И управляет занесением символов в выходной буфер. Все равно шустрее, чем использовать % и /, и, даже шустрее, чем c div_t div(int numer, int denom); (сразу % и / в одном флаконе).


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;
int flag=0;

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

Еще можно дооптимизировать, чтобы условное занесение в буфер убрать из цикла, разделив цикл на две части, ну и убрать флаг.

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;
int flag=0;
int tmp;

if (!num) {
*str_p++='0';
return;
}

while ((tmp=num_table[p++]) && (number < tmp)) ;

do {
i='0'; while ( (number-=tmp) >= 0) i++;
number += tmp;
*str_p++=i;
} while (tmp = num_table[p++]);
CrimsonPig
Цитата(SM @ Jan 21 2015, 12:17) *
То, что я дал код, переделывается на работу без нулей введением одного флага, который сначала запрещен, и разрешается при первом ненулевом символе. И управляет занесением символов в выходной буфер. Все равно шустрее, чем использовать % и /, и, даже шустрее, чем c div_t div(int numer, int denom); (сразу % и / в одном флаконе).


<зевая> int у нас какой разрядности ?

Я практически отошел от использования "просто int". Гораздо спокойнее писать "uint32_t", "uint16_t", "uint8_t" и не беспокоиться о портабельности кода на машину с другой архитектурой ЦПУ.
А еще я ненавижу знаковый int, и использую int32_t только там, где знак нужет в явном виде.
Jenya7
Цитата(SM @ Jan 21 2015, 14:45) *
Еще, если Ваш 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 с собой не притащит из либы.
И, добавлю, это быстро, если в процессоре нет аппаратного деления.


while ( (number-=num_table[p]) >= 0) всегда true.
SM
Цитата(CrimsonPig @ Jan 21 2015, 15:39) *
<зевая> int у нас какой разрядности ?

Это пусть автор разбирается. Я предпочитаю соблюдать старые добрые стандарты, где еще нет int32_t.

Цитата(Jenya7 @ Jan 21 2015, 15:45) *
while ( (number-=num_table[p]) >= 0) всегда true.

Это с какого перепуга? Тип не unsigned.
А если нужен полный диапазон unsigned, то тут, к сожалению, С не покатит, так как нужно условие по флагу переноса после математической операции, что реализуемо только на ассемблере. Оптимизация, штука тонкая....
CrimsonPig
Цитата(SM @ Jan 21 2015, 12:47) *
А если нужен полный диапазон unsigned, то тут, к сожалению, С не покатит, так как нужно условие по флагу переноса после математической операции, что реализуемо только на ассемблере. Оптимизация, штука тонкая....


если почитать книжку "Hacker's delight" (она есть и в русском переводе) или опять же погуглить, то можно нарыть методов проверки операций над целыми числами на переполнение на чистом Ц.
там битхаки, завязанные на конкретную разрядность...
SM
Цитата(CrimsonPig @ Jan 21 2015, 15:54) *
там битхаки, завязанные на конкретную разрядность...

Но это не оптимально. Разве что, только ради того, чтобы сделать это хоть как-то, но на С. Поэтому я не предлагаю таких решений в теме про оптимизацию алгоритма на скорость.
Jenya7
Цитата(SM @ Jan 21 2015, 18:47) *
Это пусть автор разбирается. Я предпочитаю соблюдать старые добрые стандарты, где еще нет int32_t.


Это с какого перепуга? Тип не unsigned.
А если нужен полный диапазон unsigned, то тут, к сожалению, С не покатит, так как нужно условие по флагу переноса после математической операции, что реализуемо только на ассемблере. Оптимизация, штука тонкая....

неа. надо допиливать функцию. с uint32_t как раз работает просто надо добавить проверку что число "закончилось".
CrimsonPig
Цитата(SM @ Jan 21 2015, 12:57) *
Но это не оптимально. Разве что, только ради того, чтобы сделать это хоть как-то, но на С. Поэтому я не предлагаю таких решений в теме про оптимизацию алгоритма на скорость.


для этого существуют debug asserts, тесты и метрики code coverage
... вон, без проверок на переполнение Ариан (или кто там) долетался... и истребитель потом кверху лапками переворачивался при полете на высоте ниже уровня моря.
SM
Цитата(Jenya7 @ Jan 21 2015, 16:00) *
надо добавить проверку что число "закончилось".

Это и есть проверка на это - если число стало отрицательным, значит конец цикла.
Для работы с unsigned это бы показал флаг переноса (carry), к сожалению, средствами С не доступный. Если же Вам хватит положительной части диапазона int32_t на все Ваши случаи, то ничего допиливать не надо, а использовать int32_t, на который рассчитан алгоритм. Если нужен полный диапазон unsigned int - то придется либо написать на ассемблере цикл, либо:

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

и прибавление number += num_table[p убить. Но это не оптимально, так как сравнение и вычитание, по своей сути, одна и та же операция, то есть, тут операций вдвое больше становится.


Цитата(CrimsonPig @ Jan 21 2015, 16:02) *
для этого существуют debug asserts, тесты и метрики code coverage

для чего? Чтобы оптимально написать алгоритм на С, который на нем оптимально написать невозможно по причине недоступности флага переноса ? sm.gif sm.gif
Jenya7
Цитата(SM @ Jan 21 2015, 19:05) *
Если же Вам хватит положительной части диапазона int32_t на все Ваши случаи, то ничего допиливать не надо, а использовать int32_t, на который рассчитан алгоритм.


и с int32_t не работает sm.gif
SM
Цитата(Jenya7 @ Jan 21 2015, 16:24) *
и с int32_t не работает sm.gif

Чего там не работает? Сейчас в MSVC 6.0 собрал для теста, все работает. Только не надо ему давать чисел, имеющих больше десятичных разрядов, чем максимальное в таблице.

CODE

#include "stdio.h"

void ItoA(char * str, int number)
{
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++=i;
}

*str_p++=0;

}


void ItoA1(char * str, int number)
{
int num_table[9] = {10000000, 1000000,100000,10000,1000,100,10,1,0};
int p=0;
char *str_p = str;
int i;
int flag=0;
int tmp;

if (!number) {
*str_p++='0';
*str_p++=0;
return;
}

while ((tmp=num_table[p++]) && (number < tmp)) ;

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

*str_p++=0;
}

int main(int argc, char* argv[])
{


int i=123456;
char str[16];
char str1[16];

ItoA(str, i);
ItoA1(str1, i);



printf("%s %s\n", str, str1);
return 0;
}


andrew_b
Цитата(CrimsonPig @ Jan 21 2015, 14:37) *
2. В цикле for (i = 0, j = strlen(s)-1; i < j; i++ , j--) есть наивное предположение, что компилятор вынесет strlen(s) за цикл как инвариант. В общем случае неверно. s передается как параметр в функцию и компилятор имеет право решить, что данные строки могут изменяться извне в другом треде, например. Поэтому с чистой совестью может оставить вызов strlen в каждой итерации цикла.
С какой стати? Это вычисляется один раз до входа в цикл. В противном случае это противоречит стандарту Си (ISO/IEC 9899, пункт 6.8.5.3), и такой компилятор надо выбросить.
AHTOXA
Цитата(Jenya7 @ Jan 21 2015, 17:03) *
так нету библиотечных функций в том то и дело. и более лучшей генерик функции я не нашел.

Выбирайте!
---
Упс, уже дали эту ссылку sm.gif
Jenya7
Цитата(SM @ Jan 21 2015, 19:28) *
Чего там не работает? Сейчас в MSVC 6.0 собрал для теста, все работает. Только не надо ему давать чисел, имеющих больше десятичных разрядов, чем максимальное в таблице.

CODE

#include "stdio.h"

void ItoA(char * str, int number)
{
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++=i;
}

*str_p++=0;

}


void ItoA1(char * str, int number)
{
int num_table[9] = {10000000, 1000000,100000,10000,1000,100,10,1,0};
int p=0;
char *str_p = str;
int i;
int flag=0;
int tmp;

if (!number) {
*str_p++='0';
*str_p++=0;
return;
}

while ((tmp=num_table[p++]) && (number < tmp)) ;

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

*str_p++=0;
}

int main(int argc, char* argv[])
{


int i=123456;
char str[16];
char str1[16];

ItoA(str, i);
ItoA1(str1, i);



printf("%s %s\n", str, str1);
return 0;
}




офигеть! таки работает! sm.gif

а вот листинг двух функций
Код
void itoa_with_leading_zeroes(int32_t number, char * str)
{
     f64:    b4b0          push    {r4, r5, r7}
     f66:    b08f          sub    sp, #60; 0x3c
     f68:    af00          add    r7, sp, #0
     f6a:    6078          str    r0, [r7, #4]
     f6c:    6039          str    r1, [r7, #0]
    int num_table[9] = {10000000, 1000000,100000,10000,1000,100,10,1,0};
     f6e:    f647 530c     movw    r3, #32012; 0x7d0c
     f72:    f2c0 0300     movt    r3, #0
     f76:    f107 0408     add.w    r4, r7, #8
     f7a:    461d          mov    r5, r3
     f7c:    cd0f          ldmia    r5!, {r0, r1, r2, r3}
     f7e:    c40f          stmia    r4!, {r0, r1, r2, r3}
     f80:    cd0f          ldmia    r5!, {r0, r1, r2, r3}
     f82:    c40f          stmia    r4!, {r0, r1, r2, r3}
     f84:    682b          ldr    r3, [r5, #0]
     f86:    6023          str    r3, [r4, #0]
      int p=0;
     f88:    f04f 0300     mov.w    r3, #0
     f8c:    637b          str    r3, [r7, #52]; 0x34
      char *str_p = str;
     f8e:    683b          ldr    r3, [r7, #0]
     f90:    633b          str    r3, [r7, #48]; 0x30
      int i;

      while (num_table[p])
     f92:    e02c          b.n    fee <itoa_with_leading_zeroes+0x8a>
      {
        i='0';
     f94:    f04f 0330     mov.w    r3, #48; 0x30
     f98:    62fb          str    r3, [r7, #44]; 0x2c
        while ( (number-=num_table[p]) >= 0) i++;
     f9a:    e003          b.n    fa4 <itoa_with_leading_zeroes+0x40>
     f9c:    6afb          ldr    r3, [r7, #44]; 0x2c
     f9e:    f103 0301     add.w    r3, r3, #1
     fa2:    62fb          str    r3, [r7, #44]; 0x2c
     fa4:    6b7b          ldr    r3, [r7, #52]; 0x34
     fa6:    ea4f 0383     mov.w    r3, r3, lsl #2
     faa:    f107 0238     add.w    r2, r7, #56; 0x38
     fae:    18d3          adds    r3, r2, r3
     fb0:    f853 3c30     ldr.w    r3, [r3, #-48]
     fb4:    687a          ldr    r2, [r7, #4]
     fb6:    1ad3          subs    r3, r2, r3
     fb8:    607b          str    r3, [r7, #4]
     fba:    687b          ldr    r3, [r7, #4]
     fbc:    2b00          cmp    r3, #0
     fbe:    daed          bge.n    f9c <itoa_with_leading_zeroes+0x38>
        number += num_table[p++];
     fc0:    6b7b          ldr    r3, [r7, #52]; 0x34
     fc2:    ea4f 0383     mov.w    r3, r3, lsl #2
     fc6:    f107 0238     add.w    r2, r7, #56; 0x38
     fca:    18d3          adds    r3, r2, r3
     fcc:    f853 3c30     ldr.w    r3, [r3, #-48]
     fd0:    687a          ldr    r2, [r7, #4]
     fd2:    18d3          adds    r3, r2, r3
     fd4:    607b          str    r3, [r7, #4]
     fd6:    6b7b          ldr    r3, [r7, #52]; 0x34
     fd8:    f103 0301     add.w    r3, r3, #1
     fdc:    637b          str    r3, [r7, #52]; 0x34
        *str_p++=i;
     fde:    6afb          ldr    r3, [r7, #44]; 0x2c
     fe0:    b2da          uxtb    r2, r3
     fe2:    6b3b          ldr    r3, [r7, #48]; 0x30
     fe4:    701a          strb    r2, [r3, #0]
     fe6:    6b3b          ldr    r3, [r7, #48]; 0x30
     fe8:    f103 0301     add.w    r3, r3, #1
     fec:    633b          str    r3, [r7, #48]; 0x30
    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])
     fee:    6b7b          ldr    r3, [r7, #52]; 0x34
     ff0:    ea4f 0383     mov.w    r3, r3, lsl #2
     ff4:    f107 0238     add.w    r2, r7, #56; 0x38
     ff8:    18d3          adds    r3, r2, r3
     ffa:    f853 3c30     ldr.w    r3, [r3, #-48]
     ffe:    2b00          cmp    r3, #0
    1000:    d1c8          bne.n    f94 <itoa_with_leading_zeroes+0x30>
        while ( (number-=num_table[p]) >= 0) i++;
        number += num_table[p++];
        *str_p++=i;
      }

      *str_p++=0;
    1002:    6b3b          ldr    r3, [r7, #48]; 0x30
    1004:    f04f 0200     mov.w    r2, #0
    1008:    701a          strb    r2, [r3, #0]
    100a:    6b3b          ldr    r3, [r7, #48]; 0x30
    100c:    f103 0301     add.w    r3, r3, #1
    1010:    633b          str    r3, [r7, #48]; 0x30
}

68 инструкций.

и мой старый метод
Код
void ItoA(uint32_t n , char s[])
{
     ef4:    b580          push    {r7, lr}
     ef6:    b084          sub    sp, #16
     ef8:    af00          add    r7, sp, #0
     efa:    6078          str    r0, [r7, #4]
     efc:    6039          str    r1, [r7, #0]
  int i = 0;
     efe:    f04f 0300     mov.w    r3, #0
     f02:    60fb          str    r3, [r7, #12]
  do
  {
    s[i++] = n % 10 + '0';
     f04:    68fb          ldr    r3, [r7, #12]
     f06:    683a          ldr    r2, [r7, #0]
     f08:    18d0          adds    r0, r2, r3
     f0a:    6879          ldr    r1, [r7, #4]
     f0c:    f64c 43cd     movw    r3, #52429; 0xcccd
     f10:    f6cc 43cc     movt    r3, #52428; 0xcccc
     f14:    fba3 2301     umull    r2, r3, r3, r1
     f18:    ea4f 02d3     mov.w    r2, r3, lsr #3
     f1c:    4613          mov    r3, r2
     f1e:    ea4f 0383     mov.w    r3, r3, lsl #2
     f22:    189b          adds    r3, r3, r2
     f24:    ea4f 0343     mov.w    r3, r3, lsl #1
     f28:    1aca          subs    r2, r1, r3
     f2a:    b2d3          uxtb    r3, r2
     f2c:    f103 0330     add.w    r3, r3, #48; 0x30
     f30:    b2db          uxtb    r3, r3
     f32:    7003          strb    r3, [r0, #0]
     f34:    68fb          ldr    r3, [r7, #12]
     f36:    f103 0301     add.w    r3, r3, #1
     f3a:    60fb          str    r3, [r7, #12]
  }
  while ((n /= 10) >= 1);
     f3c:    687a          ldr    r2, [r7, #4]
     f3e:    f64c 43cd     movw    r3, #52429; 0xcccd
     f42:    f6cc 43cc     movt    r3, #52428; 0xcccc
     f46:    fba3 1302     umull    r1, r3, r3, r2
     f4a:    ea4f 03d3     mov.w    r3, r3, lsr #3
     f4e:    607b          str    r3, [r7, #4]
     f50:    687b          ldr    r3, [r7, #4]
     f52:    2b00          cmp    r3, #0
     f54:    d1d6          bne.n    f04 <ItoA+0x10>
   //s[i] = '\0';
  Reverse(s);
     f56:    6838          ldr    r0, [r7, #0]
     f58:    f7ff ff9a     bl    e90 <Reverse>
}

void Reverse(char s[])
{
  int c, i, j;

  for (i = 0, j = strlen(s)-1; i < j; i++ , j--)
     ed4:    697b          ldr    r3, [r7, #20]
     ed6:    f103 0301     add.w    r3, r3, #1
     eda:    617b          str    r3, [r7, #20]
     edc:    693b          ldr    r3, [r7, #16]
     ede:    f103 33ff     add.w    r3, r3, #4294967295
     ee2:    613b          str    r3, [r7, #16]
     ee4:    697a          ldr    r2, [r7, #20]
     ee6:    693b          ldr    r3, [r7, #16]
     ee8:    429a          cmp    r2, r3
     eea:    dbe0          blt.n    eae <Reverse+0x1e>
    c = s[i];
    s[i] = s[j];
    s[j] = c;
  }
  //s[i] = '\0';
}

38+10 = 48 инструкций.
sm.gif

Цитата(AHTOXA @ Jan 21 2015, 19:43) *
Выбирайте!
---
Упс, уже дали эту ссылку sm.gif


да. CrimsonPig давал уже эту ссылку. есть интересное решение. надо проверить.

в принципе int num_table[9] = {10000000, 1000000,100000,10000,1000,100,10,1,0}; можно вынести из тела функции...
SM
Что-то у вас косяк с уровнем оптимизации. Это пишется в несколько команд ассемблера.

Для особо тупых компиляторов, можно еще подоптимизировать:

CODE

void ItoA(char * str, int number)
{
static int num_table[9] = {10000000, 1000000,100000,10000,1000,100,10,1,0};
int p=0;
char *str_p = str;
int i;
int tmp;

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

*str_p++=0;

}
CrimsonPig
Цитата(Jenya7 @ Jan 21 2015, 14:18) *
в принципе int num_table[9] = {10000000, 1000000,100000,10000,1000,100,10,1,0}; можно вынести из тела функции...


Зачем ? Засорять пространство имен всякими локальными именами - очень плохая идея.
Объявите этот массив внутри функции как
const int num_table[9] = {...}
Хотя нормальный компилятор и так должен был догадаться, что это константы.

SM
Цитата(CrimsonPig @ Jan 21 2015, 17:36) *
const int num_table[9] = {...}


не const, а static. Да, это мое упущение. Этому на стеке не место.

Вот что генерирует TI CCS из последнего кода (16 инструкций):

CODE

_ItoA:
;* --------------------------------------------------------------------------*
LDR V9, $C$CON1 ; |11|
.dwpsn file "ccs.c",line 12,column 0,is_stmt
LDR A4, [V9, #0] ; |11|
;* --------------------------------------------------------------------------*
;* BEGIN LOOP ||$C$L1||
;*
;* Loop source line : 12
;* Loop closing brace source line : 16
;* Known Minimum Trip Count : 1
;* Known Maximum Trip Count : 4294967295
;* Known Max Trip Count Factor : 1
;* --------------------------------------------------------------------------*
||$C$L1||:
$C$DW$L$_ItoA$2$B:
SUBS A2, A2, A4 ; |13|
MOV A3, #48 ; |13|
BMI ||$C$L3|| ; |13|
; BRANCHCC OCCURS {||$C$L3||} ; |13|
$C$DW$L$_ItoA$2$E:
;* --------------------------------------------------------------------------*
;* BEGIN LOOP ||$C$L2||
;*
;* Loop source line : 13
;* Loop closing brace source line : 13
;* Known Minimum Trip Count : 1
;* Known Maximum Trip Count : 4294967295
;* Known Max Trip Count Factor : 1
;* --------------------------------------------------------------------------*
||$C$L2||:
$C$DW$L$_ItoA$3$B:
.dwpsn file "ccs.c",line 13,column 0,is_stmt
SUBS A2, A2, A4 ; |13|
ADD A3, A3, #1 ; |13|
BPL ||$C$L2|| ; |13|
; BRANCHCC OCCURS {||$C$L2||} ; |13|
$C$DW$L$_ItoA$3$E:
;* --------------------------------------------------------------------------*
||$C$L3||:
$C$DW$L$_ItoA$4$B:
STRB A3, [A1], #1 ; |15|
ADD A2, A4, A2 ; |14|
LDR A4, [V9, #4]! ; |16|
CMP A4, #0 ; |16|
.dwpsn file "ccs.c",line 16,column 0,is_stmt
BNE ||$C$L1|| ; |16|
; BRANCHCC OCCURS {||$C$L1||} ; |16|
$C$DW$L$_ItoA$4$E:
;* --------------------------------------------------------------------------*
MOV V9, #0 ; |18|
STRB V9, [A1, #0] ; |18|
.dwpsn file "ccs.c",line 20,column 1,is_stmt
$C$DW$4 .dwtag DW_TAG_TI_branch
.dwattr $C$DW$4, DW_AT_low_pc(0x00)
.dwattr $C$DW$4, DW_AT_TI_return
BX LR
; BRANCH OCCURS {LR}
Jenya7
Цитата(AHTOXA @ Jan 21 2015, 19:43) *
Выбирайте!
---
Упс, уже дали эту ссылку sm.gif


проверил несколько функций из ссылки - листиг моего метода самый короткий.
SM
Вот еще оптимизация (все время забываю про то, что цикл while, в отличие от do, дает лишнюю команду перехода, а это есть самое зло из всех зол - переходы):

CODE

void ItoA(char * str, int number)
{
static const int num_table[9] = {10000000, 1000000,100000,10000,1000,100,10,1,0};
int p=0;
char *str_p = str;
int i;
int tmp;

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

*str_p++=0;

}


Листинг 15 инструкций sm.gif (а без *str_p++=0; всего 13)

CODE

_ItoA:
;* --------------------------------------------------------------------------*
LDR V9, $C$CON1 ; |11|
LDR A4, [V9, #0] ; |11|
.dwpsn file "ccs.c",line 13,column 0,is_stmt
MOV A3, #47 ; |13|
;* --------------------------------------------------------------------------*
;* BEGIN LOOP ||$C$L1||
;*
;* Loop source line : 13
;* Loop closing brace source line : 13
;* Known Minimum Trip Count : 1
;* Known Maximum Trip Count : 4294967295
;* Known Max Trip Count Factor : 1
;* --------------------------------------------------------------------------*
||$C$L1||:
$C$DW$L$_ItoA$2$B:
SUBS A2, A2, A4 ; |13|
ADD A3, A3, #1 ; |13|
BPL ||$C$L1|| ; |13|
; BRANCHCC OCCURS {||$C$L1||} ; |13|
$C$DW$L$_ItoA$2$E:
;* --------------------------------------------------------------------------*
$C$DW$L$_ItoA$3$B:
ADD A2, A4, A2 ; |14|
LDR A4, [V9, #4]! ; |16|
STRB A3, [A1], #1 ; |15|
CMP A4, #0 ; |16|
MOVNE A3, #47 ; |13|
BNE ||$C$L1|| ; |16|
; BRANCHCC OCCURS {||$C$L1||} ; |16|
$C$DW$L$_ItoA$3$E:
;* --------------------------------------------------------------------------*
MOV V9, #0 ; |18|
STRB V9, [A1, #0] ; |18|
.dwpsn file "ccs.c",line 20,column 1,is_stmt
$C$DW$4 .dwtag DW_TAG_TI_branch
.dwattr $C$DW$4, DW_AT_low_pc(0x00)
.dwattr $C$DW$4, DW_AT_TI_return
BX LR


так что, разбирайтесь со своим кривым компилятором, из какого пальца он там инструкции высасывает.
Jenya7
Цитата(SM @ Jan 21 2015, 20:21) *
Что-то у вас косяк с уровнем оптимизации. Это пишется в несколько команд ассемблера.

Для особо тупых компиляторов, можно еще подоптимизировать:


эээ...я тут вспомнил что у меня атолик - фри версия...мля...нулевая оптимизация...все мои листинги не годяться. извиняюсь товарищи.
CrimsonPig
Цитата(SM @ Jan 21 2015, 14:40) *
не const, а static. Да, это мое упущение. Этому на стеке не место.


Почему static-то ?. Думаю, что компилятору будет сильно проще понять, что если int переменная объявлена как const, то выделять память для нее совсем не надо.
Кроме того, если вдруг кто-нибудь скопипастит этот код в Ц++, то следует ожидать некоторых сюрпризов:

static переменные внутри функций Ц++ инициализирует _первом_ входе управления в область видимости этой переменной.
http://stackoverflow.com/questions/55510/w...ted-initialized

То есть для Ц++ память будет выделена и переменная будет инициализирована при первом вызове Foo.
void Foo()
{
static int A = 13;
<use A>
}

думаю, что тут память вообще не будет выделяьтся
void Foo()
{
const int A = 13;
static const int B=4;
<use A, B>
}



Цитата(Jenya7 @ Jan 21 2015, 14:50) *
эээ...я тут вспомнил что у меня атолик - фри версия...мля...нулевая оптимизация...все мои листинги не годяться. извиняюсь товарищи.


Недаром, один из отцов-основателей сказал: "premature opimisation is a root of all evil"
SM
Цитата(CrimsonPig @ Jan 21 2015, 17:53) *
Почему static-то

Потому, что любые переменные, не объявленные статическими, но объявленные внутри функции, располагаются на стеке, независимо от того, const они, или нет. И инициализируются динамически после вызова функции. Се ля ви, так сказать.


-------------
для ТС:

А вот Вам Ваш код, тем же компилятором, с той же оптимизацией. Сравнивайте с 15-ю инструкциями.

CODE

_Reverse:
;* --------------------------------------------------------------------------*
SUB A2, A1, #1 ; |94|
STMFD SP!, {V1, V2, V3, LR}
LDRB A3, [A2, #1]! ; |96|
.dwpsn file "C:/CCStudio_v3.3/tms470_4.6.2/include/string.h",line 96,column 0,is_stmt
MVN V9, #0 ; |93|
;* --------------------------------------------------------------------------*
;* BEGIN LOOP ||$C$L1||
;*
;* Loop source line : 96
;* Loop closing brace source line : 96
;* Known Minimum Trip Count : 1
;* Known Maximum Trip Count : 4294967295
;* Known Max Trip Count Factor : 1
;* --------------------------------------------------------------------------*
||$C$L1||:
$C$DW$L$_Reverse$2$B:
CMP A3, #0 ; |96|
LDRNEB A3, [A2, #1]! ; |96|
ADD V9, V9, #1 ; |96|
BNE ||$C$L1|| ; |96|
; BRANCHCC OCCURS {||$C$L1||} ; |96|
$C$DW$L$_Reverse$2$E:
;* --------------------------------------------------------------------------*
SUBS A3, V9, #1 ; |97|
LDMLEFD SP!, {V1, V2, V3, PC}
; BRANCHCC OCCURS {[SP, #0]}
;* --------------------------------------------------------------------------*
ADD V3, V9, V9, LSR #31 ; |36|
CMP V9, #4 ; |36|
MOV A4, V3, ASR #2 ; |36|
MOV LR, #0 ; |34|
MOV V9, A1
BLT ||$C$L3|| ; |36|
; BRANCHCC OCCURS {||$C$L3||} ; |36|
;* --------------------------------------------------------------------------*
ADD A2, A3, V9
LDRB V1, [A2, #0] ; |37|
.dwpsn file "ccs.c",line 34,column 0,is_stmt
LDRB V2, [V9, #0] ; |34|
;* --------------------------------------------------------------------------*
;* BEGIN LOOP ||$C$L2||
;*
;* Loop source line : 34
;* Loop closing brace source line : 39
;* Loop Unroll Multiple : 2x
;* Known Minimum Trip Count : 1
;* Known Maximum Trip Count : 536870911
;* Known Max Trip Count Factor : 1
;* --------------------------------------------------------------------------*
||$C$L2||:
$C$DW$L$_Reverse$6$B:
.dwpsn file "ccs.c",line 35,column 0,is_stmt
STRB V1, [V9], #1 ; |37|
STRB V2, [A2], #-1 ; |38|
LDRB V1, [A2, #0] ; |37|
LDRB V2, [V9, #0] ; |36|
SUBS A4, A4, #1 ; |34|
STRB V1, [V9], #1 ; |37|
STRB V2, [A2], #-1 ; |38|
LDRNEB V1, [A2, #0] ; |37|
LDRNEB V2, [V9, #0] ; |34|
ADD LR, LR, #2 ; |36|
SUB A3, A3, #2 ; |37|
.dwpsn file "ccs.c",line 39,column 0,is_stmt
BNE ||$C$L2|| ; |34|
; BRANCHCC OCCURS {||$C$L2||} ; |34|
$C$DW$L$_Reverse$6$E:
;* --------------------------------------------------------------------------*
||$C$L3||:
TST V3, #2
LDRNEB V9, [A1, +A3] ; |37|
LDMEQFD SP!, {V1, V2, V3, PC}
; BRANCHCC OCCURS {[SP, #0]}
;* --------------------------------------------------------------------------*
; Peeled loop iterations for unrolled loop:
LDRB A2, [A1, +LR] ; |36|
STRB V9, [A1, +LR] ; |37|
STRB A2, [A1, +A3] ; |38|
.dwpsn file "ccs.c",line 41,column 1,is_stmt
$C$DW$3 .dwtag DW_TAG_TI_branch
.dwattr $C$DW$3, DW_AT_low_pc(0x00)
.dwattr $C$DW$3, DW_AT_TI_return
LDMFD SP!, {V1, V2, V3, PC}
; BRANCH OCCURS {[SP, #0]}



CODE

_ItoA1:
;* --------------------------------------------------------------------------*
STMFD SP!, {V1, V2, V3, LR}
LDR A3, $C$CON1 ; |48|
.dwpsn file "ccs.c",line 46,column 0,is_stmt
MOV A4, A2
;* --------------------------------------------------------------------------*
;* BEGIN LOOP ||$C$L4||
;*
;* Loop source line : 46
;* Loop closing brace source line : 49
;* Known Minimum Trip Count : 1
;* Known Maximum Trip Count : 4294967295
;* Known Max Trip Count Factor : 1
;* --------------------------------------------------------------------------*
||$C$L4||:
$C$DW$L$_ItoA1$2$B:
.dwpsn file "ccs.c",line 47,column 0,is_stmt
UMULL LR, V9, A3, A1 ; |48|
MOV V9, V9, LSR #3 ; |48|
SUB A1, A1, V9, LSL #3 ; |48|
SUB A1, A1, V9, LSL #1 ; |48|
ADD LR, A1, #48 ; |48|
MOVS A1, V9 ; |50|
STRB LR, [A4], #1 ; |48|
MVNEQ V9, #0 ; |93|
.dwpsn file "ccs.c",line 49,column 0,is_stmt
BNE ||$C$L4|| ; |50|
; BRANCHCC OCCURS {||$C$L4||} ; |50|
$C$DW$L$_ItoA1$2$E:
;* --------------------------------------------------------------------------*
SUB A1, A2, #1 ; |94|
.dwpsn file "C:/CCStudio_v3.3/tms470_4.6.2/include/string.h",line 96,column 0,is_stmt
LDRB A3, [A1, #1]! ; |96|
;* --------------------------------------------------------------------------*
;* BEGIN LOOP ||$C$L5||
;*
;* Loop source line : 96
;* Loop closing brace source line : 96
;* Known Minimum Trip Count : 1
;* Known Maximum Trip Count : 4294967295
;* Known Max Trip Count Factor : 1
;* --------------------------------------------------------------------------*
||$C$L5||:
$C$DW$L$_ItoA1$4$B:
CMP A3, #0 ; |96|
LDRNEB A3, [A1, #1]! ; |96|
ADD V9, V9, #1 ; |96|
BNE ||$C$L5|| ; |96|
; BRANCHCC OCCURS {||$C$L5||} ; |96|
$C$DW$L$_ItoA1$4$E:
;* --------------------------------------------------------------------------*
SUBS A3, V9, #1 ; |97|
LDMLEFD SP!, {V1, V2, V3, PC}
; BRANCHCC OCCURS {[SP, #0]}
;* --------------------------------------------------------------------------*
ADD V3, V9, V9, LSR #31 ; |36|
CMP V9, #4 ; |36|
MOV A4, V3, ASR #2 ; |36|
MOV LR, #0 ; |34|
MOV V9, A2
BLT ||$C$L7|| ; |36|
; BRANCHCC OCCURS {||$C$L7||} ; |36|
;* --------------------------------------------------------------------------*
ADD A1, A3, V9
LDRB V1, [A1, #0] ; |37|
.dwpsn file "ccs.c",line 34,column 0,is_stmt
LDRB V2, [V9, #0] ; |34|
;* --------------------------------------------------------------------------*
;* BEGIN LOOP ||$C$L6||
;*
;* Loop source line : 34
;* Loop closing brace source line : 39
;* Loop Unroll Multiple : 2x
;* Known Minimum Trip Count : 1
;* Known Maximum Trip Count : 536870911
;* Known Max Trip Count Factor : 1
;* --------------------------------------------------------------------------*
||$C$L6||:
$C$DW$L$_ItoA1$8$B:
.dwpsn file "ccs.c",line 35,column 0,is_stmt
STRB V1, [V9], #1 ; |37|
STRB V2, [A1], #-1 ; |38|
LDRB V1, [A1, #0] ; |37|
LDRB V2, [V9, #0] ; |36|
SUBS A4, A4, #1 ; |34|
STRB V1, [V9], #1 ; |37|
STRB V2, [A1], #-1 ; |38|
LDRNEB V1, [A1, #0] ; |37|
LDRNEB V2, [V9, #0] ; |34|
ADD LR, LR, #2 ; |36|
SUB A3, A3, #2 ; |37|
.dwpsn file "ccs.c",line 39,column 0,is_stmt
BNE ||$C$L6|| ; |34|
; BRANCHCC OCCURS {||$C$L6||} ; |34|
$C$DW$L$_ItoA1$8$E:
;* --------------------------------------------------------------------------*
||$C$L7||:
TST V3, #2
LDRNEB V9, [A2, +A3] ; |37|
LDMEQFD SP!, {V1, V2, V3, PC}
; BRANCHCC OCCURS {[SP, #0]}
;* --------------------------------------------------------------------------*
; Peeled loop iterations for unrolled loop:
LDRB A1, [A2, +LR] ; |36|
STRB V9, [A2, +LR] ; |37|
STRB A1, [A2, +A3] ; |38|
.dwpsn file "ccs.c",line 53,column 1,is_stmt
$C$DW$11 .dwtag DW_TAG_TI_branch
.dwattr $C$DW$11, DW_AT_low_pc(0x00)
.dwattr $C$DW$11, DW_AT_TI_return
LDMFD SP!, {V1, V2, V3, PC}
; BRANCH OCCURS {[SP, #0]}
CrimsonPig
Цитата(SM @ Jan 21 2015, 15:02) *
Потому, что любые переменные, не объявленные статическими, но объявленные внутри функции, располагаются на стеке, независимо от того, const они, или нет. И инициализируются динамически после вызова функции. Се ля ви, так сказать.


Припадаем к источникам:
http://stackoverflow.com/questions/93039/w...s-stored-in-c-c

Какая разница с т.з. потребляемой RAM, если компилятор решит разместить automatic variable на стеке или такую же static в .DATA или .BSS ?
В то время как const имеет больше шансов стать просто числом (по аналогии с "#define A (23)" )
SM
Цитата(CrimsonPig @ Jan 21 2015, 18:09) *
Какая разница


Две большие разницы. На стек надо каждый раз при входе в фунцию предопределенные данные копировать. В .data они попадают на этапе загрузки программы один раз. А в .const (или .rodata), так они, вообще могут браться прямо из флеш-памяти прямой адресацией туда. Таким образом, имеем оверхед на копирование массива констант в стек, если без static, но с const. А вот с static, и, все равно, с const или без, не имеем этого оверхеда. Причем, проверено совсем недавно тут - http://electronix.ru/forum/index.php?showtopic=125422

Цитата(CrimsonPig @ Jan 21 2015, 17:53) *
В то время как const имеет больше шансов стать просто числом (по аналогии с "#define A (23)" )

Давайте ближе к теме, в сторону не уходить. У нас массив, по которому проводится доступ по индексу. А не просто число.
Jenya7
Цитата(CrimsonPig @ Jan 21 2015, 20:53) *
Недаром, один из отцов-основателей сказал: "premature opimisation is a root of all evil"


ничего себе premature. три месяца пишу на фри версии. sm.gif
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.