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

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

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


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

Если вернуться к sprintf. Функция работает несколько медленно из-за своей универсальности.

1. Проверить, есть ли в опциях линкера и оптимизатора настройка "мощности" printf. Чем меньше "наворотов", тем быстрее будет обработка.

2. Если есть исходник стандартной С библиотки для Вашего компилятора - можно посмотреть тотже (s)printf и урезать его до требуемого быстродействия и функц-ти.

вот заголовок специализированной ф-ии, "вытянутый" из printf

static char *float_conversion(MEM_ATTRIBUTE long double value,
                              MEM_ATTRIBUTE short nr_of_digits,
                              MEM_ATTRIBUTE char *buf,
                              MEM_ATTRIBUTE char format_flag,
                              MEM_ATTRIBUTE char g_flag,
                              MEM_ATTRIBUTE char alternate_flag)

IAR, MSP430, frmwri.c

 

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


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

вот мой вариант

static inline int IntToStr(int x, char str[], int d)
{
    int i = 0;
    
     do { str[i++] = (x % 10) + '0'; }
     while ((x /= 10) > 0);
     
    // If number of digits required is more, then
    // add 0s at the beginning
    while (i < d)
        str[i++] = '0';

    Reverse(str, i);
    str[i] = '\0';
    return i;
}

// Converts a floating point number to string.
void FtoA(float n, char *res, int afterpoint)
{
    int ap = afterpoint;
    
     //signed?
    if(n < 0)
    {
        n *= -1;
        *res = '-';
        res++;
    }
    
    // Extract integer part
    int ipart = (int)n;
    
    // Extract floating part
    float fpart = n - (float)ipart;
        
    // convert integer part to string
    int i = IntToStr(ipart, res, 0);

    // check for display option after point
    if (afterpoint != 0)
    {
        res[i] = '.';  // add dot

        while (ap)
        {
            fpart *= 10;
            ap--;
        }

        IntToStr((int)fpart, res + i + 1, afterpoint);
    }
}

 

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


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

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...