• Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

Научите считать float

nikolz

Well-known member
Ну как, если до этого число 12.3 это в int 0x4144cccd или1095027917 или x1=52429 x2=16708, и преобразование работает правильно, то просто присвоить тип float дает на выходе 1095027968.0
Ну все верно
Вы преобразовали число 1095027917 из формата int в формат float.
Число не изменилось, а изменилось его внутреннее представление. т е если посмотрите двоичное представление байт числа то обнаружите и мантиссу и порядок в доп коде с удаленным знаком. Т е внутреннее представление у Int и float этого числа разное.
Вы что хотите ?
------------------------------
 

Urbas81

Member
Ну все верно
Вы преобразовали число 1095027917 из формата int в формат float.
Число не изменилось, а изменилось его внутреннее представление. т е если посмотрите двоичное представление байт числа то обнаружите и мантиссу и порядок в доп коде с удаленным знаком. Т е внутреннее представление у Int и float этого числа разное.
Вы что хотите ?
------------------------------
А как внутреннее представление посмотреть? Мне нужно на вход функции подать 2 int на выходе получить float которое я могу записывать в файл или вывести в лог, именно в формате 12.3 так как это сделано тут IEEE-754 Floating Point Converter, только в обратную сторону, для тех int которые дают на выходе float >1 все OK, а если число меньше 1 зависает, скорее всего из-за нехватки времени на возведение в степень.


Код:
double pow(double x,double y)
{
    double z , p=1;
  if(y<0)
    z = fabs(y);
else
    z = y;
for(int i=0; i<z ; ++i)
{
    p *= x;
}
if(y<0)
   return 1/p;
else
   return p;
}



float ICACHE_FLASH_ATTR int_float(uint32 x1, uint32 x0)
{


            uint32 value = (x1<<16) | x0;
            uint32 b=value & 0x7fffff;
            double e=((value>>23) & 0xff)-127;
            double m=1+b*pow(2, -23);
           
            float p=pow(2.0, e);
            float result=m*p;
            if (value & 0x80000000) {
            result=-result;
            }
            return result;
    }
 
А как внутреннее представление посмотреть? Мне нужно на вход функции подать 2 int на выходе получить float которое я могу записывать в файл или вывести в лог, именно в формате 12.3 так как это сделано тут IEEE-754 Floating Point Converter, только в обратную сторону, для тех int которые дают на выходе float >1 все OK, а если число меньше 1 зависает, скорее всего из-за нехватки времени на возведение в степень.
Для начала, os_prinf в esp не поддерживает fp форматы, функцию вывода придется писать самому. Это раз. Два, возводить 2 в степень надо сдвигом влево, ну или вправо, если это отрицательная степень. Ну и три, зачем нужен этот "закат солнца вручную"? Связываться с fp для отсутствующего в библиотеке его вывода? Бессмысленно. Или считайте все в fp, или считайте в целых, а выводите отдельно целую, отдельно дробную части os_printf("%d.%d", mantissa, exponenet); Если считаете в fp, то вывод его не то чтобы сложен, но пописать придется. Я написал вывод вида "1.234k" для числа 1234.0, но там если посмотреть понятно как выводить и в других форматах. Ну оно в строку выводит, потом ее напечатать куда-то надо. Ну и преобразовать целое в fp надо не побитно руками, а просто double d = i+j/100.0; если i - целая часть, а j - сотые.
 

Вложения

nikolz

Well-known member
А как внутреннее представление посмотреть? Мне нужно на вход функции подать 2 int на выходе получить float которое я могу записывать в файл или вывести в лог, именно в формате 12.3 так как это сделано тут IEEE-754 Floating Point Converter, только в обратную сторону, для тех int которые дают на выходе float >1 все OK, а если число меньше 1 зависает, скорее всего из-за нехватки времени на возведение в степень.
Можете рассказать что Вы делаете?
---------------------------
Пока из Вашего смутного рассказа я понял, что у вас есть число записанное в формате float, но в программе вы его получили в виде uint32.
а вы хотите чтобы было float.
Если так, то просто смените тип
float y=(float)x; где х - это uint32 но в нем число в формате float.
т е у Вас код не меняется а меняется тип переменной в которой этот код.
------------------------
Вам это надо?
 

Urbas81

Member
Для начала, os_prinf в esp не поддерживает fp форматы, функцию вывода придется писать самому. Это раз. Два, возводить 2 в степень надо сдвигом влево, ну или вправо, если это отрицательная степень. Ну и три, зачем нужен этот "закат солнца вручную"? Связываться с fp для отсутствующего в библиотеке его вывода? Бессмысленно.
Все что надо и так выводится и печатается, я же написал для чисел >1 все норм.

Или считайте все в fp, или считайте в целых, а выводите отдельно целую, отдельно дробную части os_printf("%d.%d", mantissa, exponenet); Если считаете в fp, то вывод его не то чтобы сложен, но пописать придется. Я написал вывод вида "1.234k" для числа 1234.0, но там если посмотреть понятно как выводить и в других форматах. Ну оно в строку выводит, потом ее напечатать куда-то надо. Ну и преобразовать целое в fp надо не побитно руками, а просто double d = i+j/100.0; если i - целая часть, а j - сотые.
Да, это если есть целые и сотые, вот я и пытаюсь получить целые и сотые, 2 int это пара регистров Modbus в формате float, на их появление я не могу влиять.
По поводу сдвига влево/вправо эту информацию я и ждал, буду смотреть, спасибо.
 

Urbas81

Member
Можете рассказать что Вы делаете?
---------------------------
Пока из Вашего смутного рассказа я понял, что у вас есть число записанное в формате float, но в программе вы его получили в виде uint32.
Наоборот, есть два регистра Modbus в формате int являющиеся передаваемой переменной в формате float, и из них надо собрать float.
 

nikolz

Well-known member
Наоборот, есть два регистра Modbus в формате int являющиеся передаваемой переменной в формате float, и из них надо собрать float.
union {
uint32 i[2];
double a;
}x;
x.i[0]=reg1; x.i[1]=reg2;
x.a -это double (8байт)
========================
если надо float (4 байта то)

union {
uint16 i[2];
float a;
}x;
 

Urbas81

Member
union {
uint32 i[2];
double a;
}x;
x.i[0]=reg1; x.i[1]=reg2;
x.a -это double (8байт)
========================
если надо float (4 байта то)

union {
uint16 i[2];
float a;
}x;
Да, это оно спасибо, уже и с отрицательными заработало, единственно заметил не всегда корректно "-" отображает и иногда разряды теряются, подскажите как правильно перевести в строку, до этого я делал так:
Код:
char*  fts(char* buffer, float value)
{
  os_sprintf_fd(buffer, "%d.%d,", (int)(value),(int)(fabs((value - (int)value)*1000)));
  return buffer;
}
 

nikolz

Well-known member
Да, это оно спасибо, уже и с отрицательными заработало, единственно заметил не всегда корректно "-" отображает и иногда разряды теряются, подскажите как правильно перевести в строку, до этого я делал так:
Код:
char*  fts(char* buffer, float value)
{
  os_sprintf_fd(buffer, "%d.%d,", (int)(value),(int)(fabs((value - (int)value)*1000)));
  return buffer;
}
-------------------------------
хорошо бы проверить правильно ли вы поняли формат регистров.
В некоторых датчиках используется формат в виде целой (один регистр) и дробной части(второй регистр) это не формат float.
и его надо преобразовывать иначе.
--------------------------------------
 
Все что надо и так выводится и печатается, я же написал для чисел >1 все норм.
Да, это если есть целые и сотые, вот я и пытаюсь получить целые и сотые, 2 int это пара регистров Modbus в формате float, на их появление я не могу влиять.
Если в modbus это 32хбитные float, то это тип float у ESP, скорее всего стандартный IEEE-754, просто делаете

union
{
float f;
uint32_t i;
} v;

пишите v.i = modbus[n]; (если у вас там 32хбитное целое) и v.f - ваш float. Если 2 16тибитных, то v.i = ((uint32_t)modbus[n]<<16) +modbus[n+1]; (ну или наоборот, если порядок другой) Если битовый тип в modbus другой, тогда да, придется манипулировать этими битами, запихивать мантиссу, знак, порядок и его знак на свои места. Но обычно 32битный fp везде стандартный.
 
Да, это оно спасибо, уже и с отрицательными заработало, единственно заметил не всегда корректно "-" отображает и иногда разряды теряются, подскажите как правильно перевести в строку, до этого я делал так:
Я пару сообщений назад прицепил код для перевода fp в строки. Там есть с округлением тонкости, надо на один десятичный разряд вперед смотреть, чтобы правильно округлять.
 

Urbas81

Member
Я пару сообщений назад прицепил код для перевода fp в строки. Там есть с округлением тонкости, надо на один десятичный разряд вперед смотреть, чтобы правильно округлять.
Добрый день, использую Ваш функцию для печати float все норм, но на определенных значениях есть провалы/выбросы значений, я так понимаю это и есть "тонкость" как это обойти, я использую задание кол-ва знаков после запятой в диапазоне от 0 до 3, вчера попробовал стороннее решение, на нем все норм, но иногда появляются буквы/знаки после запятой.
 
Добрый день, использую Ваш функцию для печати float все норм, но на определенных значениях есть провалы/выбросы значений, я так понимаю это и есть "тонкость" как это обойти, я использую задание кол-ва знаков после запятой в диапазоне от 0 до 3, вчера попробовал стороннее решение, на нем все норм, но иногда появляются буквы/знаки после запятой.
Там, вроде, все просто и прозрачно, можно примеры чисел, на которых возникают проблемы?
 

Urbas81

Member
Там, вроде, все просто и прозрачно, можно примеры чисел, на которых возникают проблемы?
Насколько я понял, это может происходить на разных числах, как пример первый график, на нем четко видно округление до целых чисел, прямо "забор" прослеживается, на синусоиде все более предсказуемо, я выделил красным одну из частей где это происходит. Возможно это связано с точностью самого float. Для проверки нашел другой пример по выводу float, на нем эта же синусоида без искажений, но там количество знаков после запятой не регулируется.
 

Вложения

Насколько я понял, это может происходить на разных числах, как пример первый график, на нем четко видно округление до целых чисел, прямо "забор" прослеживается, на синусоиде все более предсказуемо, я выделил красным одну из частей где это происходит. Возможно это связано с точностью самого float. Для проверки нашел другой пример по выводу float, на нем эта же синусоида без искажений, но там количество знаков после запятой не регулируется.
Набросал тестик

Код:
#include <stdio.h>
#include <math.h>
#include "strnum.h"

void main(void)
{ 
char str1[256];
char str2[256];
FILE *fo = fopen("sin.txt", "wt");
if (fo)
  {
   for(int i = 0; i<360; i++)
    {
     float f = sin(i*M_PI/180.0);
     d2scistrup(str1, f, "", 3, 0);
     sprintf(str2, "%1.3f", f);
     fprintf(fo, "%i;%s;%s;\n", i, str1, str2);
    }
   fclose(fo);
}  
}
Вроде, проблем не вижу. Проверял в bcb6.
 

Вложения

Сверху Снизу