cf7k 0 25 сентября, 2007 Опубликовано 25 сентября, 2007 · Жалоба А не сталкивался ли кто-либо с потребностью преобразования вещественно числа в строку? Понятное дело - 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 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Kolia 0 26 сентября, 2007 Опубликовано 26 сентября, 2007 · Жалоба Ну если вы переходите на такие заморочки :07: . Я бы порекомендовал вам вообще отказаться от FLOAT математики и перейти к чисмам INT, LONG и т.д. ... и жизнь покажется вам сказкой :beer: Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
zltigo 0 26 сентября, 2007 Опубликовано 26 сентября, 2007 · Жалоба работает достаточно неспешно :) Поскольку человек считывает значения еще более неспешно, то одним из вероятных путей решения этой проблемы является пересмотр построения системы, дабы эта неспешность не находилась в узком месте. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
alexander55 0 26 сентября, 2007 Опубликовано 26 сентября, 2007 · Жалоба А не сталкивался ли кто-либо с потребностью преобразования вещественно числа в строку? Сталкивался. Пусть надо 2 знака после запятой. Решал примерно так. float a=1.23; int b=(int)(a*100); cout <<"a=" <<b/100 <<"," <<b%100 ; Получается полегче. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
cf7k 0 26 сентября, 2007 Опубликовано 26 сентября, 2007 · Жалоба Ну если вы переходите на такие заморочки :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х битах заблудился... клинит меня Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
zltigo 0 26 сентября, 2007 Опубликовано 26 сентября, 2007 · Жалоба В принципе даже с sprintf'ом на полной частоте можно конвертировать до сотни измерений в сек. Повторяю - предлагаю задуматься, кому нужна эта сотня измерений в секунду в текстовом виде. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
cf7k 0 26 сентября, 2007 Опубликовано 26 сентября, 2007 · Жалоба Повторяю - предлагаю задуматься, кому нужна эта сотня измерений в секунду в текстовом виде. сотня - это на 40 MIPS, а это под сотню мА на MCU. пускаю MCU на 4 MIPS - имею низкое потребление и достаточные для неспешного человека 10 измерений. пусть 4 канала - все комфортно... и все!!! более ничего!!! а мне есть чем еще занять контроллер. Ответ - это нужно мне. PS1: меня более интересует решение исходной задачи, нежели аргументы в пользу отказа от решения. PS2: мне очень стыдно признаться, что когда-то давным давно я эту чудо-задачу реализовал на ассемблере для hitachi 3672. Но в связи с преждевременной кончиной проекта - все кануло в лету. С учетом того что вещ. числа мне приходили извне и никакой плавающей математики не требовалось я смог обогнать sprintf в 25раз по скорости и в 10 раз по размеру (sprintf тянул за собой столько кода, что мне практически не оставалось флэша). Идею помню, сырцов нет, попытка повторить успех забуксовала... вот и предположил, что кто-нибудь тоже мог до такого додуматься и я бы смог разжиться кусочком готового кода. :laughing: Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
cf7k 0 1 октября, 2007 Опубликовано 1 октября, 2007 · Жалоба Раз никто не изъявил желания помочь :) - вот то, что я хотел изначально. Даже без оптимизации и выпендрежа ускорение минимум в 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 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
chingochguk 0 20 ноября, 2018 Опубликовано 20 ноября, 2018 · Жалоба Спасибо! Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
k155la3 26 29 декабря, 2018 Опубликовано 29 декабря, 2018 · Жалоба Если вернуться к 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 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jenya7 0 9 июля, 2019 Опубликовано 9 июля, 2019 · Жалоба вот мой вариант 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); } } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться