Перейти к содержанию

    

Float To Str

А не сталкивался ли кто-либо с потребностью преобразования вещественно числа в строку?

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

 

нашел даже красивый вариант:

http://lua-users.org/lists/lua-l/2004-11/msg00466.html

 

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

 

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

 

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

 

из 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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ну если вы переходите на такие заморочки :07: . Я бы порекомендовал вам вообще отказаться от FLOAT математики и перейти к чисмам INT, LONG и т.д. ... и жизнь покажется вам сказкой :beer:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
работает достаточно неспешно :)

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
А не сталкивался ли кто-либо с потребностью преобразования вещественно числа в строку?

Сталкивался.

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

 

float a=1.23;

int b=(int)(a*100);

cout <<"a=" <<b/100 <<"," <<b%100 ;

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
Ну если вы переходите на такие заморочки :07: . Я бы порекомендовал вам вообще отказаться от FLOAT математики и перейти к чисмам INT, LONG и т.д. ... и жизнь покажется вам сказкой :beer:

 

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

 

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

 

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

 

 

Сталкивался.

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

 

float a=1.23;

int b=(int)(a*100);

cout <<"a=" <<b/100 <<"," <<b%100 ;

 

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

 

Спасибо. Идея ясна. Но пусть float a = 1.23e-7 :) В приведенном в исходном посте примере я не показал вывод показателся степени, но он подразумевается.

Когда числа "похожи" на целые двузначные (примерно от 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:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
В принципе даже с sprintf'ом на полной частоте можно конвертировать до сотни измерений в сек.

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

Поделиться сообщением


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

 

сотня - это на 40 MIPS, а это под сотню мА на MCU.

пускаю MCU на 4 MIPS - имею низкое потребление и достаточные для неспешного человека 10 измерений.

пусть 4 канала - все комфортно...

и все!!! более ничего!!!

а мне есть чем еще занять контроллер.

 

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

 

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

 

PS2: мне очень стыдно признаться, что когда-то давным давно я эту чудо-задачу реализовал на ассемблере для hitachi 3672. Но в связи с преждевременной кончиной проекта - все кануло в лету.

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

 

Идею помню, сырцов нет, попытка повторить успех забуксовала... вот и предположил, что кто-нибудь тоже мог до такого додуматься и я бы смог разжиться кусочком готового кода. :laughing:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

 

Даже без оптимизации и выпендрежа ускорение минимум в 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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Для публикации сообщений создайте учётную запись или авторизуйтесь

Вы должны быть пользователем, чтобы оставить комментарий

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!

Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.

Войти
Авторизация