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

 
 
 
Reply to this topicStart new topic
> Float To Str, очень быстренькое преобразование
cf7k
сообщение Sep 25 2007, 12:27
Сообщение #1


Частый гость
**

Группа: Свой
Сообщений: 82
Регистрация: 14-03-06
Из: Санкт-Петербург
Пользователь №: 15 227



А не сталкивался ли кто-либо с потребностью преобразования вещественно числа в строку?
Понятное дело - sprintf (&buf, "%F",....) в большинстве случаев эту проблему решает. Но есть несколько "но": эта ф-ция тянет за собой много математики и работает достаточно неспешно smile.gif

нашел даже красивый вариант:
http://lua-users.org/lists/lua-l/2004-11/msg00466.html

Но все это тянет математику...

Я готов пожертвовать пару килобайт ПЗУ под таблички для ускорения процесса smile.gif

А моя идея такова:

из IEEE-754 взять показатель степени (2^N), вычислить его в формате К*10^Y и хранить две таблички: K и Y, а вся математика сведется к умножению 2х fixed-point чисел.

Z=A*2^N; 2^N = K*10^Y => Z=A*K*10^Y.

и далее домножать на 10 и вытаскивать из старших разрядов очередную цифру.

только вот мои эксперименты к успешному результату никак не хотят приводить.
(в примере ниже все экспериментально)

Код
typedef union _floats {
  unsigned long u;
  float f;
  struct {
    unsigned ml : 16;
    unsigned mh : 7;
    signed   e  : 8;
    unsigned s  : 1;
  };
} floats;


void convert1 (float in, char *dst)
{
#define shift 24
#define mask 0x00FFFFFF;

  floats intf;
  float lg10;
  floats exp2;
  unsigned long ires;
  unsigned char i;

  intf.f = in;
  exp2.u = 0;                         //заземлить :)
  exp2.e = intf.e;

  exp2.f = log10(exp2.f);         //Ex: 2^-17 = 10^-5.11751
  lg10   = floorf(exp2.f);          //10^-5.11751 = (10^-6)*(10^0.88249)
  exp2.f = exp2.f - lg10;
  exp2.f = powf(10, exp2.f);    //10^.088249 = 7.629395
  
  unsigned long long res, mul1, mul2;

  mul1 = intf.u & 0x00FFFFFF;    //1.23
  mul1 = mul1 | 0x800000;       //set hidden "1"
  mul1 = mul1 << 1;                //1.24

  mul2 = exp2.u & 0x00FFFFFF;  //1.23
  mul2 = mul2 | 0x800000;       //set hidden "1"
  mul2 = mul2 << 1;                //1.24

  res = mul1 * mul2;                //целая часть IN [1,2,...,9]
  ires = res >> 24;

   ires = ires * 10;
  i = ires >> shift;
  *dst++ = i + '0';
  
  *dst++ = '.';

  ires &= mask;
  ires = ires * 10;
  i = ires >> shift;
  *dst++ = i + '0';

  ires &= mask;
  ires = ires * 10;
  i = ires >> shift;
  *dst++ = i + '0';
}


Где ж все-таки собака порылась ?

Эти чудеса нацелены на PIC24.
(меня устраивает, что это только для чисел одинарной точности, не универсально; нечисла мне по барабану)
Прикрепленные файлы
Прикрепленный файл  Table2Calc.rar ( 19.64 килобайт ) Кол-во скачиваний: 51
 
Go to the top of the page
 
+Quote Post
Kolia
сообщение Sep 26 2007, 09:55
Сообщение #2


Частый гость
**

Группа: Свой
Сообщений: 188
Регистрация: 28-09-06
Из: Minsk
Пользователь №: 20 762



Ну если вы переходите на такие заморочки 07.gif . Я бы порекомендовал вам вообще отказаться от FLOAT математики и перейти к чисмам INT, LONG и т.д. ... и жизнь покажется вам сказкой beer.gif
Go to the top of the page
 
+Quote Post
zltigo
сообщение Sep 26 2007, 10:35
Сообщение #3


Гуру
******

Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244



Цитата(cf7k @ Sep 25 2007, 15:27) *
работает достаточно неспешно smile.gif

Поскольку человек считывает значения еще более неспешно, то одним из вероятных путей решения этой проблемы является пересмотр построения системы, дабы эта неспешность не находилась в узком месте.


--------------------
Feci, quod potui, faciant meliora potentes
Go to the top of the page
 
+Quote Post
alexander55
сообщение Sep 26 2007, 11:42
Сообщение #4


Бывалый
*****

Группа: Свой
Сообщений: 1 584
Регистрация: 7-08-07
Пользователь №: 29 615



Цитата(cf7k @ Sep 25 2007, 16:27) *
А не сталкивался ли кто-либо с потребностью преобразования вещественно числа в строку?

Сталкивался.
Пусть надо 2 знака после запятой. Решал примерно так.

float a=1.23;
int b=(int)(a*100);
cout <<"a=" <<b/100 <<"," <<b%100 ;

Получается полегче.
Go to the top of the page
 
+Quote Post
cf7k
сообщение Sep 26 2007, 15:10
Сообщение #5


Частый гость
**

Группа: Свой
Сообщений: 82
Регистрация: 14-03-06
Из: Санкт-Петербург
Пользователь №: 15 227



Цитата(Kolia @ Sep 26 2007, 13:55) *
Ну если вы переходите на такие заморочки 07.gif . Я бы порекомендовал вам вообще отказаться от FLOAT математики и перейти к чисмам INT, LONG и т.д. ... и жизнь покажется вам сказкой beer.gif


Я бы с радостью wink.gif Только вот трудоемкость перевода всей математики, калибровочных кривых, и пр. никак не сделают жизнь сказкой. Вся математика уже написана и не мной.

Цитата(zltigo @ Sep 26 2007, 14:35) *
Поскольку человек считывает значения еще более неспешно, то одним из вероятных путей решения этой проблемы является пересмотр построения системы, дабы эта неспешность не находилась в узком месте.


Тут я уже пооптимиздил smile.gif В принципе даже с sprintf'ом на полной частоте можно конвертировать до сотни измерений в сек. Но задача в другом - вписаться в бюджет по питанию, дабы не ставить еще один (или более мощный) DC-DC. Вся обработка одного канала измерений занимает времени столько же сколько и конвертирование float в строку. А у меня еще виртуальная машина кушать хочет smile.gif


Цитата(alexander55 @ Sep 26 2007, 15:42) *
Сталкивался.
Пусть надо 2 знака после запятой. Решал примерно так.

float a=1.23;
int b=(int)(a*100);
cout <<"a=" <<b/100 <<"," <<b%100 ;

Получается полегче.


Спасибо. Идея ясна. Но пусть float a = 1.23e-7 smile.gif В приведенном в исходном посте примере я не показал вывод показателся степени, но он подразумевается.
Когда числа "похожи" на целые двузначные (примерно от 0.1 до 99) - ту все понятно, ну а если динамический диапазон немножко шире - приходится выводить в экспоненциальной форме.

для простоты выпендрежа я делал так:
Код
//-------------------------------------------------------------------------------------------------
//описание формата 3хкомпонентного представления числа
//-------------------------------------------------------------------------------------------------
typedef struct _Value3{
  char  Exp;      //кол-во порядков
  char  M_int;          //целая часть мантиссы
  char  M_1;        //первая цифра дробной части мантиссы
  float Tens;
}Value3, *PValue3;


void FloatToNum3 ( float Num, PValue3 Num3)
{
  float Mantissaf, Expf, M_intf;
  Expf        = floorf( log10f( Num ) );     //целое количество порядков в числе
  Num3->Exp   = (char)Expf;
  Num3->Tens  = powf( 10, Expf );
  Mantissaf   = Num/(Num3->Tens);                //исх. число в формате [1.0..9.9)
  M_intf      = floorf( Mantissaf );    
  Num3->M_int = (char) M_intf;
  Num3->M_1   = (char)floorf( (Mantissaf - M_intf)*10 );
}


Но это по производительности соизмеримо с библиотечым sprintf'ом.

Хотя бы ткните в линк, где бы подробненько были разжеваны правила работы с fixed-point, а то между двух чисел блуждаю... в 23х битах заблудился... клинит меня wacko.gif
Go to the top of the page
 
+Quote Post
zltigo
сообщение Sep 26 2007, 15:18
Сообщение #6


Гуру
******

Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244



Цитата(cf7k @ Sep 26 2007, 18:10) *
В принципе даже с sprintf'ом на полной частоте можно конвертировать до сотни измерений в сек.

Повторяю - предлагаю задуматься, кому нужна эта сотня измерений в секунду в текстовом виде.


--------------------
Feci, quod potui, faciant meliora potentes
Go to the top of the page
 
+Quote Post
cf7k
сообщение Sep 26 2007, 16:46
Сообщение #7


Частый гость
**

Группа: Свой
Сообщений: 82
Регистрация: 14-03-06
Из: Санкт-Петербург
Пользователь №: 15 227



Цитата(zltigo @ Sep 26 2007, 19:18) *
Повторяю - предлагаю задуматься, кому нужна эта сотня измерений в секунду в текстовом виде.


сотня - это на 40 MIPS, а это под сотню мА на MCU.
пускаю MCU на 4 MIPS - имею низкое потребление и достаточные для неспешного человека 10 измерений.
пусть 4 канала - все комфортно...
и все!!! более ничего!!!
а мне есть чем еще занять контроллер.

Ответ - это нужно мне. biggrin.gif

PS1: меня более интересует решение исходной задачи, нежели аргументы в пользу отказа от решения.

PS2: мне очень стыдно признаться, что когда-то давным давно я эту чудо-задачу реализовал на ассемблере для hitachi 3672. Но в связи с преждевременной кончиной проекта - все кануло в лету.
С учетом того что вещ. числа мне приходили извне и никакой плавающей математики не требовалось я смог обогнать sprintf в 25раз по скорости и в 10 раз по размеру (sprintf тянул за собой столько кода, что мне практически не оставалось флэша).

Идею помню, сырцов нет, попытка повторить успех забуксовала... вот и предположил, что кто-нибудь тоже мог до такого додуматься и я бы смог разжиться кусочком готового кода. laughing.gif
Go to the top of the page
 
+Quote Post
cf7k
сообщение Oct 1 2007, 10:04
Сообщение #8


Частый гость
**

Группа: Свой
Сообщений: 82
Регистрация: 14-03-06
Из: Санкт-Петербург
Пользователь №: 15 227



Раз никто не изъявил желания помочь smile.gif - вот то, что я хотел изначально.

Даже без оптимизации и выпендрежа ускорение минимум в 10 раз.

некоторые "моменты" я еще не протестировал... но концептуально все работает.

Пользуйтесь на здоровье, и да совершат ваши процессоры больше полезной работы. CF7k

Код
const unsigned long TwoEXPTable[];
const signed char TenEXPTable[];

typedef union _floats {
  unsigned long u;
  float f;
  struct {
    unsigned ml : 16;
    unsigned mh : 7;
    signed   e  : 8;
    unsigned s  : 1;
  };
} floats;

typedef union _ullongs {
  unsigned long long ull;
  struct{
    unsigned long ll;  
    unsigned char f3,f2,f1;
    unsigned char i;
  };
  struct {
    unsigned long LL;
    unsigned long HL;
  };
}ullongs;

typedef struct _strbuf {
  signed char bufidx;
  signed char exp;
  unsigned char M[8];  //M[0] - целая часть, далее дробная
}strbuf;

unsigned char maxdigs = 2;
#define maxconvdigs 6

char *convert (float in, char *dst)
{
  static strbuf buf;
  floats fin;
  unsigned char exp2;
  ullongs res;
  
  unsigned char digcnt = 0;
  unsigned char add = 0;

  fin.f = in;
  if (fin.s) *dst++ = '-';
  exp2 = fin.e;
  fin.s = 0;
  fin.e = 1;          //set hidden "1"
  fin.u = fin.u << 1; //format fixed-point 8.24;

  res.ull = (unsigned long long)(fin.u) *
            (unsigned long long)(TwoEXPTable[exp2]);
  res.ull = res.ull << 8;
  buf.exp = TenEXPTable[exp2];
  buf.bufidx = 0;
  if (res.i >=10)                   //normalize + exp10(+1)
  {
    res.i +=6;                      //hex to BCD
    buf.M[buf.bufidx++] = res.i >>0x04;
    buf.exp += 1;
  }
  buf.M[buf.bufidx] = res.i & 0x0F;

  //сконвертировать на 1 цифру больше чем нужно цифр
  for (digcnt = 1; digcnt <= maxconvdigs; digcnt++)
  {
    res.i = 0;
    res.HL = res.HL * 10;
    buf.M[++buf.bufidx] = res.i;
  }
  //округление от последнего нужного разряда
  buf.bufidx = maxdigs+1;

  if (buf.M[buf.bufidx]>=5) add=1;
  while ((buf.bufidx >= 0) && add)
  {
    add += buf.M[buf.bufidx];
    buf.M[buf.bufidx--] = add % 10;
    add = add / 10;
  }
  //вывод в текстовом виде
  buf.bufidx = 1;
  *dst++ = buf.M[0] + '0';
  *dst++ = '.';
  while ( buf.bufidx <= maxdigs )
    *dst++ = buf.M[buf.bufidx++]+'0';
  *dst++ = 'e';
  if (buf.exp<0) {*dst++ = '-'; buf.exp = - buf.exp;}
  else *dst++ = '+';
  
  if (buf.exp > 9) {*dst++=buf.exp/10 + '0'; *dst++=buf.exp%10 + '0';}
  else *dst++ = buf.exp + '0';

  return dst;
}


Таблички прилагаются.
Прикрепленные файлы
Прикрепленный файл  table.rar ( 3.48 килобайт ) Кол-во скачиваний: 107
 
Go to the top of the page
 
+Quote Post

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

 


RSS Текстовая версия Сейчас: 18th July 2025 - 08:29
Рейтинг@Mail.ru


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