помогите оптимизировать код по размеру

Обсуждаем контроллеры компании Atmel.
Ответить
Встал на лапы
Сообщения: 98
Зарегистрирован: Пн дек 06, 2010 12:26:45

Сообщение ESWANT »

Нужно написать функцию для преобразования байта в строку
например a=5 str='5' a=255 str='255'
причем иногда нужно доводить длину строки до 2 или 3-х символов требуемого размера дополняя слева нулями
например a=5 str='05' либо a=27 str='027'

первый вариант - две разные функци для фиксированных длин строк
если дополнять нулями не нужно то использую просто sprintf:

Код: Выделить всё

void Byte_To_ASCII_BCD_2 (byte b, unsigned char *str)
{
unsigned char buf[4];   // '2' '5' '5' '\0'        

 sprintf (buf,"%u",b);
 if ((strlen(buf)==0)|(strlen(buf)==3)) {*str='0';    str=str+1;*str='0';    }//этот случай если sprintf выдаст пустую строку (хотя такого быть не должно так как даже на ноль должно быть '0'
 if (strlen(buf)==1) {*str='0';    str=str+1;*str=buf[0]; }
 if (strlen(buf)==2) {*str=buf[0]; str=str+1;*str=buf[1]; }
 
 
str=str+1;*str='\0';
}//proc


void Byte_To_ASCII_BCD_3 (byte b, unsigned char *str)
{
unsigned char buf[4];   // '2' '5' '5' '\0'        

 sprintf (buf,"%u",b);
 if (strlen(buf)==0) {*str='0';    str=str+1;*str='0';  str=str+1;*str='0';   }//этот случай если sprintf выдаст пустую строку (хотя такого быть не должно так как даже на ноль должно быть '0'
 if (strlen(buf)==1) {*str='0';    str=str+1;*str='0';  str=str+1;*str=buf[0]; }
 if (strlen(buf)==2) {*str='0';    str=str+1;*str=buf[0]; str=str+1;*str=buf[1]; }
 if (strlen(buf)==3) {*str=buf[0]; str=str+1;*str=buf[1]; str=str+1;*str=buf[2]; }
 
 
str=str+1;*str='\0';
}//proc

и второй вариант - решил отказаться то библиотечной sprintf
а код получился еще больше
честно говоря я его еще и не проверял - может он и не рабочий

Код: Выделить всё


//превращает байт в строку, поэтому для str достаточно 4 байта размера
//f-  0-без ведущих нулей
//    1-3 - обязательная длина (добавлять ведущие нули)
    
void ByteToStr(byte f,byte b, unsigned char *str) 
{    
byte sotni,desatki,edinici;

*str = _T('\0');

sotni = b/100; //255/100=2       5/100 = 0
desatki = ( b - (sotni*100) ) / 10;//    (255-(2*100)) / 10 =5        5-(0*100) /10 = 0
edinici = ( b - (sotni*100) - (desatki*10) ) % 10; //( 255 - (2*100) - (5*10) ) % 10;    
                      
                       //для случая без ведущих нулей
if (   (f==3) | ( (f==0)&(sotni!=0) )    )            
    {
      *str = sotni+0x30; str=str+1; 
      *str = desatki+0x30; str=str+1;   
      *str = edinici+0x30; str=str+1;   
      *str = _T('\0');
     return;
     }
                       //для случая без ведущих нулей
if (   (f==2) | ( (f==0)&(sotni==0)&(desatki!=0) )    )   
   { 
     *str = desatki+0x30; str=str+1;   
     *str = edinici+0x30; str=str+1;   
     *str = _T('\0');
     return;
    }
                     //для случая без ведущих нулей
if (   (f==1) | ( (f==0)&(sotni==0)&(desatki==0)&(edinici!=0) )    )      
   {
     *str = edinici+0x30; str=str+1;   
    *str = _T('\0');
    return;
   }

//если число ноль то 
if  (   (f==0)&(sotni==0)&(desatki==0)&(edinici==0) )     
      {
        *str=_T('0'); str=str+1;  
        *str = _T('\0');
       }


}//proc




Что посоветуете?
Последний раз редактировалось ESWANT Вт сен 02, 2014 12:56:44, всего редактировалось 1 раз.
Реклама
Друг Кота
Аватара пользователя
Сообщения: 4732
Зарегистрирован: Ср сен 18, 2013 10:08:26
Откуда: Санкт-Петербург

Сообщение Gisteresis »

Посоветую попробовать сделать массив символов так, чтобы указатель совпадал с символом. {'0','1','2','3','4'...'9'}
Тогда обращаясь к массиву мы автоматически конвертируем цифру в символ и убирается куча ифов, которые кстати лучше было бы оформить через свич.

Кроме того можно попробовать попросить компилятор оптимизировать по размеру.
Реклама
Встал на лапы
Сообщения: 98
Зарегистрирован: Пн дек 06, 2010 12:26:45

Сообщение ESWANT »

Не понял
Саму цифру в символ я конвертирую прибавляя 0x30 и все
Друг Кота
Аватара пользователя
Сообщения: 4732
Зарегистрирован: Ср сен 18, 2013 10:08:26
Откуда: Санкт-Петербург

Сообщение Gisteresis »

ESWANT писал(а):Саму цифру в символ я конвертирую прибавляя 0x30 и все
Хм... это лучше предложенного мной. Не подумал сразу.

Можно вместо ввода длины, определять ее в коде. Если число больше 9 но меньше 100 ясно, что символов 2. для остальных вариантов нужна всего одна проверка.
Код это не уменьшит, но можно сделать удобнее применени этой функции. 1 параметр тогда ненужно вводить.

Тогда такая вот идея.
Можно попробовать ифы заменить на цикл. Нужно создать массив на 3 байта и записать туда единицы, десятки, сотни, прогоняем в цикле и компонуем строку. Тогда бы эти ифы ужались бы в общий код.
Реклама
Эиком - электронные компоненты и радиодетали
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18786
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

Код: Выделить всё

#include <string.h>
#include <stdlib.h>

// предельное количество знаков в строке
#define MAXLEN  3

void byte_to_str(char s[], unsigned char fill, unsigned char byte){
   unsigned char i, len;

   itoa(byte,s,10); // получили 10-е число в виде строки
   if(!fill) return; // если не надо добавлять нули слева - конец работы

   len = strlen(s); // вычислили длину строки
   memmove(s+MAXLEN-len, s, len); // передвинули символы в конец строки
   s[MAXLEN] = 0; // поставили терминатор в конец строки
   // в цикле дополняем нулями слева полученное число
   while(MAXLEN-len){
      s[MAXLEN-len-1] = '0';
      len++;
   }
}
как-то так...
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Реклама
Встал на лапы
Сообщения: 98
Зарегистрирован: Пн дек 06, 2010 12:26:45

Сообщение ESWANT »

Gisteresis писал(а):
ESWANT писал(а):Саму цифру в символ я конвертирую прибавляя 0x30 и все
Тогда такая вот идея.
Можно попробовать ифы заменить на цикл. Нужно создать массив на 3 байта и записать туда единицы, десятки, сотни, прогоняем в цикле и компонуем строку. Тогда бы эти ифы ужались бы в общий код.
Вот что получилось

Код: Выделить всё


void ByteToStr(byte f,byte b, unsigned char *str) 
{    
byte m[3];//под числа разрядов
          //[0]-единицы
          //[1]-десятки
          //[2]-сотни

byte begin;


m[2] = b/100;                                  //СОТНИ 255/100=2       5/100 = 0
m[1] = ( b - (m[2]*100) ) / 10;                //ДЕСЯТКИ  (255-(2*100)) / 10 =5        5-(0*100) /10 = 0
m[0] = ( b - (m[2]*100) - (m[1]*10) ) % 10;    //ЕДИНИЦЫ( 255 - (2*100) - (5*10) ) % 10;    

if (f) begin=(f-1);//начинаем от требуемого разряда 
else begin =2;//если не нужны ведущие нули то проверяем с сотен и до единиц

*str = _T('\0');//ноль используем как флаг что ничего в строку еще не добавили, чтобы пропускать только ведущие нули а если внутри ноль то его добавлять строку  
                //поэтому гарантированно обнулим

for (i=begin;i>=0;i--) 
    {

      ИЛИ строка без ведущих нулей нужна И уже есть первый символ
     if(    (f) |  //если указан фиксированный размер ИЛИ
  
          //без вед.нулей   не ноль  или  ноль и не первый                 
          (  (f==0)  &   (  (m[i])  |  ((m[i]==0)&(*str))   )   )
       )

     { 
      *str=m[i]+0x30; 
      str++;
     }


    }//for

*str = _T('\0');//закончим строку

}//proc

получается просто если бы не надо предусматривать вариант без ведущих нулей
может оптимальнее две функции сделать?
одна для фиксированных длин строк
а другая для - без ведущих нулей
Реклама
Друг Кота
Аватара пользователя
Сообщения: 4732
Зарегистрирован: Ср сен 18, 2013 10:08:26
Откуда: Санкт-Петербург

Сообщение Gisteresis »

Так а по весу то стало меньше?

Ну если использовать обе функции в программе то на обе нужно место, поэтому решайте сами воспользоваться ли этой возможностью.
Когда нули не нужны вообще в программе, то определенно имеет смысл сделать такую функцию.

Еще мне кажется можно попробовать выжать из этого что то:

Код: Выделить всё

m[2] = b/100;                                  //СОТНИ 255/100=2       5/100 = 0
m[1] = ( b - (m[2]*100) ) / 10;                //ДЕСЯТКИ  (255-(2*100)) / 10 =5        5-(0*100) /10 = 0
m[0] = ( b - (m[2]*100) - (m[1]*10) ) % 10;    //ЕДИНИЦЫ( 255 - (2*100) - (5*10) ) % 10;    
Смотрите, есть переменная, которую нужно преобразовать, ее можно тоже в цикле делить на десять и инициализировать массив. При этом код для всех декад становится одинаков и позволит оформить данное преобразование в виде цикла.
Не знаю будет ли выгодно для 3х декад но от 10ти и выше точно выгода будет. Нужно пробовать.
Последний раз редактировалось Gisteresis Вт сен 02, 2014 15:11:38, всего редактировалось 2 раза.
Встал на лапы
Сообщения: 98
Зарегистрирован: Пн дек 06, 2010 12:26:45

Сообщение ESWANT »

ARV писал(а):

Код: Выделить всё

#include <string.h>
#include <stdlib.h>

// предельное количество знаков в строке
#define MAXLEN  3

void byte_to_str(char s[], unsigned char fill, unsigned char byte){
   unsigned char i, len;

   itoa(byte,s,10); // получили 10-е число в виде строки
   if(!fill) return; // если не надо добавлять нули слева - конец работы

   len = strlen(s); // вычислили длину строки
   memmove(s+MAXLEN-len, s, len); // передвинули символы в конец строки
   s[MAXLEN] = 0; // поставили терминатор в конец строки
   // в цикле дополняем нулями слева полученное число
   while(MAXLEN-len){
      s[MAXLEN-len-1] = '0';
      len++;
   }
}
как-то так...
а если byte = 255
а передали s размером всего для двух байт + для \0
и fill =2
переполнение же будет?

содержимое s при выходе из функции должно сохраниться
вы быстрей написали вариант
что скажете о моем?
Встал на лапы
Сообщения: 98
Зарегистрирован: Пн дек 06, 2010 12:26:45

Сообщение ESWANT »

Gisteresis писал(а):Еще мне кажется можно попробовать выжать из этого что то:

Код: Выделить всё

m[2] = b/100;                                  //СОТНИ 255/100=2       5/100 = 0
m[1] = ( b - (m[2]*100) ) / 10;                //ДЕСЯТКИ  (255-(2*100)) / 10 =5        5-(0*100) /10 = 0
m[0] = ( b - (m[2]*100) - (m[1]*10) ) % 10;    //ЕДИНИЦЫ( 255 - (2*100) - (5*10) ) % 10;    
Смотрите, есть переменная, которую нужно преобразовать, ее можно тоже в цикле делить на десять и инициализировать массив. При этом код для всех декад становится одинаков и позволит оформить данное преобразование в виде цикла.
Не знаю будет ли выгодно для 3х декад но от 10ти и выше точно выгода будет. Нужно пробовать.
Компилировать попробую только дома
Про вычисление разрядов тоже подумаю, хотя для байта может и не выгодно
так то мне еще надо и int преобразовывать в строку но там только без ведущих нулей
я там использую sprintf
может там еще сэкономить и полностью уйти от библиотечных
так как мне весь функционал sprintf и не нужен,
только там опять отрицательные числа хорошо делаются
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18786
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

ESWANT писал(а): а если byte = 255
а передали s размером всего для двух байт + для \0
и fill =2
переполнение же будет?

содержимое s при выходе из функции должно сохраниться
вы быстрей написали вариант
что скажете о моем?
1. MAXLEN определяет как длину результата, так и размер буфера под него. то есть вы обязаны определить в свой программе массив str[MAXLEN+1] для передачи его в функцию.
2. массив s принимает результат работы функции, как его по-вашему надо сохранять при выходе?!
3. ваш вариант не хочу анализировать, для меня слишком сложно.
4. если для sprintf использовать нужный формат, он может и "левые нули" добавлять. изучайте форматы :)))
Последний раз редактировалось ARV Вт сен 02, 2014 15:25:21, всего редактировалось 1 раз.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Друг Кота
Аватара пользователя
Сообщения: 4732
Зарегистрирован: Ср сен 18, 2013 10:08:26
Откуда: Санкт-Петербург

Сообщение Gisteresis »

Код: Выделить всё

 itoa(byte,s,10); // получили 10-е число в виде строки
А itoa сколько сожрет? Я так понял место критично, поэтому пляски с собственной функцией.

Для предложенного варианта AVR можно в теле функции задаваться не макросом а длиной переданной строки. Тогда не будет краха проги. Это для варианта, если длина заранее неизвестна.

ПС: Если вы вообще мазохист, то вам следует пройтись по форумам ассемблерщиков, там такие чудеса творят... я такой математики до них не видывал... и код запихают в любое место, любого размера...
Последний раз редактировалось Gisteresis Вт сен 02, 2014 15:35:50, всего редактировалось 4 раза.
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18786
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

Gisteresis писал(а):

Код: Выделить всё

 itoa(byte,s,10); // получили 10-е число в виде строки
А itoa сколько сожрет? Я так понял место критично, поэтому пляски с собственной функцией.

Для предложенного варианта AVR можно в теле функции задаваться не макросом а длиной переданной строки. Тогда не будет краха проги. Это для варианта, если длина заранее неизвестна.
моя функция вместе с простейшим main для теста сожрала около 380 байт (WinAVR).
в теле функции невозможно узнать, буфер какого размера передан в функцию для приема результата - это проблема программиста. использование в программе для задания размера буфера макроса MAXLEN+1 гарантирует, что переполнений не будет.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Друг Кота
Аватара пользователя
Сообщения: 4732
Зарегистрирован: Ср сен 18, 2013 10:08:26
Откуда: Санкт-Петербург

Сообщение Gisteresis »

ARV писал(а):в теле функции невозможно узнать, буфер какого размера передан в функцию для приема результата - это проблема программиста.
Тупанул, подумал, что у нас есть модуль динамического выделения памяти как в ОС :))) Вы правы.
Встал на лапы
Сообщения: 98
Зарегистрирован: Пн дек 06, 2010 12:26:45

Сообщение ESWANT »

Попробовал мой(рожденный на форуме) вариант - стало на 20 слов меньше
15185-16165

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

по поводу переполнения Вы меня запутали рассуждениями
я имею ввиду что:
передается байт(unsigned char) - там число может быть от 0 до 255
и передается указатель на (ну фактически на набор байтов) строку
она может быть обьявлена unsigned char str[4] или unsigned char[3]
(в последнем элементе место под '\0')
опять же для экономии лишний байт не выделяю :)))

тоесть при вызове функции не известно сколько места есть
И передавая f я гарантирую что только f-символов будет в строке

причем исходный байт поступает из 'вне' неизвестно с каким значением
например byte=255, а по логике программы мне нужно сгенерировать только 2 символа
в моем алгоритме результат будет '55' и никакого превышения

Спасибо, так-то помогло уменьшить размер
правда ещё логику работы не проверял, но надеюсь всё нормально преобразовывается

Попробую потом расширить на int (вдруг получится меньше чем sprintf)
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18786
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

создается впечатление, что опыт программирования МК у вас неболшой, а вот код - что-то подозрительно велик... вы оптимизацию включили вообще?
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Встал на лапы
Сообщения: 98
Зарегистрирован: Пн дек 06, 2010 12:26:45

Сообщение ESWANT »

ARV писал(а):создается впечатление, что опыт программирования МК у вас неболшой, а вот код - что-то подозрительно велик... вы оптимизацию включили вообще?
Впечатление всегда обманчиво, все же люди разные, :)))
Опыт вещь относительная, что то использовал, что то не довелось...
Я далеко не гуру, конечно...
Код действительно большой Mega32 под завязку....
Причем он достаточно оптимальный...
Проект успешно работает у меня с 2007 года,
успел поменять аппаратную платформу с 1878ВЕ1 на PIC16... на Mega16 и наконец на Mega32
И всё это никогда не мешает мне задавать дурацкие вопросы :)))
Поставщик валерьянки для Кота
Аватара пользователя
Сообщения: 1900
Зарегистрирован: Сб фев 21, 2009 13:11:40
Откуда: Москва

Сообщение ibiza11 »

ESWANT писал(а):Причем он достаточно оптимальный...
Это Вы как определили?

Код: Выделить всё

__flash uint8_t deg[] = {
   100,
   10,
   1 };

void bin2ascii(char * str, uint8_t value, uint8_t zeroes){
   uint8_t i, digit_value;
   for(i = 0; i <= 2; i++){
      digit_value = 0;
      while(value >= deg[i]){
         value -= deg[i];
         digit_value++;
      }
      *str = digit_value + '0';
      if(!(digit_value)){
         if(zeroes){
            str++;
         }
      } else {
         str++;
      }
   }
   str++;
   *str = 0;
}
как Вам такой вариант?
Ставим плюсы: )
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18786
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

к чему все эти извраты в виде попытки оказаться от умножения/деления? крайне маловероятно, что в программе не используются операции деления/умножения, а значит, подпрограммы этих операций в коде уже присутствуют - почему бы не использовать их? если человек ползуется sprintf-ами, то и к быстродействию особых требований не предъявляется... да и операция деления в конечном итоге тоже вычитаниями/сложениями реализована наверняка...
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Встал на лапы
Сообщения: 98
Зарегистрирован: Пн дек 06, 2010 12:26:45

Сообщение ESWANT »

ibiza11 писал(а): как Вам такой вариант?
Спасибо
Попробую обязательно, сравню, напишу результат
Щас только въеду в логику Вашу
Встал на лапы
Сообщения: 98
Зарегистрирован: Пн дек 06, 2010 12:26:45

Сообщение ESWANT »

ARV писал(а):к чему все эти извраты в виде попытки оказаться от умножения/деления? ...
Да действительно умножение деление есть да еще и с long int

А чё sprintf интересно насколько тяжеловесная,
может от нее вообще отказаться ...
Ответить

Вернуться в «AVR»