Если ваш вопрос не влез ни в одну из вышеперечисленных тем, вам сюда.
Ответить

Как хранить double значения в int?

Сб ноя 30, 2019 14:14:49

Написал код для опроса датчика SHT30. Контроллер STM32F303VC.
Всё работает, но я задался таким вопросом. Как переделать функцию чтоб данные хранились не с плавающей точкой, а как целое число?
Например в int хранится температура в виде "2646", что равно реальной температуре "26,46". В даташите к датчикам BMP280 есть пример но как сделать именно для SHT30 не пойму.
Подскажите как выполнять такие преобразования не используя типы с плавающей точкой?
Спойлер
Код:
   float Tc; //температура в градусах цельсия
   float Tf; //температура в фаренгейтах
   float RH; //влажность в %

   uint8_t data[6] = { 0 };
   uint16_t St, Srh;

   St = (data[0] << 8) | data[1]; //значение температуры, полученное с датчика SHT30
   Tc = -45.0 + 175.0 * ((float) St / 65535.0);
   Tf = -49.0 + 315.0 * ((float) St / 65535.0);

   Srh = (data[3] << 8) | data[4]; //значение влажности, полученное с датчика SHT30
   RH = 100.0 * ((float) Srh / 65535.0);

P.S. поисковиком пользовался - ничего похожего не нашел.
P.P.S. это не реальная необходимость, а скорее из "спортивного" интереса.

Re: Как хранить double значения в int?

Сб ноя 30, 2019 14:29:06

Воспользуйтесь libfixmath
Там для такого преобразования inline функции fix16_from_float() b fix16_from_double() в libfixmath/fix16.h.

Re: Как хранить double значения в int?

Сб ноя 30, 2019 14:40:19

вообще не надо никаких преобразований.
после вывода "26" сам принудительно выводишь десятичную точку и потом выводишь оставшиеся цифры ("46").

Re: Как хранить double значения в int?

Сб ноя 30, 2019 15:44:09

Я, видимо, объяснил не понятно. Постараюсь по другому объяснить.
Я получил с датчика какое-то число St (uint16_t).
Подставляю это число в формулу Tc = -45 + 175 * (St / 65535), где Tc например int32_t.
Если я буду подставлять как есть, то после вычислений в Tc будет -45 т.к. St < 65535.
Вопрос в том что мне необходимо изменить в формуле чтоб получить реальное значение в виде "2646"? и возможно вообще это сделать или нет?

Re: Как хранить double значения в int?

Сб ноя 30, 2019 16:31:56

Привести к uint32 и умножить все на 100.
Пример для одного знака после запятой.

Re: Как хранить double значения в int?

Сб ноя 30, 2019 16:40:53

Tc=-45L<<16+175L*St
В старших двух байтах Tc будет целая часть. В младших - дробная. Значение будет завышено, примерно, на 0.0001%

Re: Как хранить double значения в int?

Сб ноя 30, 2019 17:02:24

Ellissar писал(а):Я, видимо, объяснил не понятно
как спросил, так я и ответил.
Ellissar писал(а):Tc = -45 + 175 * (St / 65535)
делить надо на 65536.
на ассемблере это делается вообще без плавающей точки, только в целых числах.
2 байта умножаются на 2 байта и от 4-байтового результата отбрасываются 2 младших байта, что эквивалентно делению на 65536.

Re: Как хранить double значения в int?

Сб ноя 30, 2019 17:27:19

Я, видимо, объяснил не понятно. Постараюсь по другому объяснить.
Я получил с датчика какое-то число St (uint16_t).
Подставляю это число в формулу Tc = -45 + 175 * (St / 65535),

Эдди Вам чуть ранее ответил, но почему то привел объемный пример вместо одной строки:
Tc = -45 + 175 * ((uint32_t)St / 65536).

Re: Как хранить double значения в int?

Сб ноя 30, 2019 17:38:35

уже был ответ одной строкой:
ПростоНуб писал(а):Tc=-45L<<16+175L*St

Re: Как хранить double значения в int?

Сб ноя 30, 2019 17:54:13

уже был ответ одной строкой:
ПростоНуб писал(а):Tc=-45L<<16+175L*St
И он неправильный. Правильный будет: Tc=(-45l<<16)+175ul*St

Добавлено after 5 minutes 8 seconds:
Эдди Вам чуть ранее ответил, но почему то привел объемный пример вместо одной строки:
Tc = -45 + 175 * ((uint32_t)St / 65536).
St у ТС-а - 16-битное целое. А значит результат данной строки всегда будет == -45.

Re: Как хранить double значения в int?

Сб ноя 30, 2019 19:45:17

Tc=-45L<<16+175L*St

Что-то не то. При значении St = 0x66CF у меня Tc = 0x00 :dont_know:

КРАМ писал(а):Tc = -45 + 175 * ((uint32_t)St / 65536).

Это я пробовал, но всегда возвращается -45. Если я правильно понимаю то при делении St на 65536 получается 0. St всегда меньше 65536.

Добавлено after 13 minutes 36 seconds:
Правильный будет: Tc=(-45l<<16)+175ul*St

Я что-то где-то упускаю, видимо. Не получается.
при St = 0x670e
float у меня вышло = 25.448616
а Tc = 1667730 Tc - uint32_t
И ещё один вопрос. Как в uint32_t будут храниться отрицательные значения? Лучше наверное int32_t использовать?

Re: Как хранить double значения в int?

Сб ноя 30, 2019 19:55:42

uint32_t unsigned то есть, беззнаковый, то есть, отрицательные там не хранятся. Признаком отрицательности является самый левый бит, что дает возможность записывать абсолютные значения либо больше в 2 раза (unsigned), либо меньше (signed)

Re: Как хранить double значения в int?

Сб ноя 30, 2019 20:08:26

Что-то не то. При значении St = 0x66CF у меня Tc = 0x00 :dont_know:
Так и должно быть. То выражение - неверное.

Если я правильно понимаю то при делении St на 65536 получается 0. St всегда меньше 65536.
Правильно понимаете. И то выражение - неверное. :)

Правильный будет: Tc=(-45l<<16)+175ul*St
Я что-то где-то упускаю, видимо. Не получается.
при St = 0x670e
float у меня вышло = 25.448616
а Tc = 1667730 Tc - uint32_t
А как считаете?
Ввёл сейчас как в приведённой формуле в калькуляторе: (0x670e*175-45*2^16)/2^16 получил = 25.4475 - всё ок.
1667730 - это число в форме fixed-point. Т.е. точка там находится в позиции между 15-м и 16-м битами.
Чтобы получить из него целые градусы и их доли нужно сделать:
Код:
uint32 i = ABS32(Tc) + (1u << 15) / 100u; //ABS32() - находит модуль 32-битного числа
uint r0 = i >> 16;                //целые градусы
uint r1 = (i & 65535u) * 100u >> 16; //сотые доли градуса
А потом воспользоваться советом Starichok51 из первого поста:
printf("%c%u.%02u", ((int32)Tc >> 31 & '-' - '+') + '+', r0, r1);

Как в uint32_t будут храниться отрицательные значения? Лучше наверное int32_t использовать?
Для Tc? Можно и int32_t. Как хранить - не важно, важно как потом использовать его.

Re: Как хранить double значения в int?

Вс дек 01, 2019 13:34:15

А как считаете?

Я в отладчике смотрю. (TrueSTUDIO)
Ввёл сейчас как в приведённой формуле в калькуляторе: (0x670e*175-45*2^16)/2^16 получил = 25.4475 - всё ок.

По такой формуле: Tc = ((-45L<<16) + 175L * St) >> 16; у меня получается при St = 0x6543 по формуле из даташита float = 24.2229347, а по выше приведённой формуле int32_t = 24 ровно. Но хотелось бы увидеть в int32_t значение 2422 как в примерах к BMP280.

Похоже догадываюсь откуда у меня непонимание вопроса. Я кроме названия мало что знаю о fixed-point. Буду читать :)

Re: Как хранить double значения в int?

Вс дек 01, 2019 14:05:01

По такой формуле: Tc = ((-45L<<16) + 175L * St) >> 16; у меня получается при St = 0x6543 по формуле из даташита float = 24.2229347, а по выше приведённой формуле int32_t = 24 ровно. Но хотелось бы увидеть в int32_t значение 2422 как в примерах к BMP280.
Вышеприведённая формула даёт целые градусы. А полностью - целые и доли - я выше написал как получить.

Похоже догадываюсь откуда у меня непонимание вопроса. Я кроме названия мало что знаю о fixed-point. Буду читать :)
И это правильно. :beer:

PS: Почитайте ещё это: http://en.wikipedia.org/wiki/Q_(number_format) - это из той же оперы. Только в общем случае десятичная точка может стоять в любой позиции, где удобно, даже за пределами разрядной сетки регистра. И может перемещаться в результате арифметических операций с числами.

Re: Как хранить double значения в int?

Вс дек 01, 2019 15:51:31

Starichok51 писал(а):делить надо на 65536.
прошу прощение. сейчас посмотрел даташит на эту микру, да, делить надо на 65535.
но можно пренебречь вычитаем единички, на точность это практически не повлияет, и тогда делать просто сдвиг вправо 16 раз.
Ellissar писал(а):Но хотелось бы увидеть в int32_t значение 2422
делается это так:
берем не 175, а 17500. и берем не 45, а 4500.
тогда получаем результат сразу в сотых долях градуса.
а далее, как я говорил выше - при выводе на экран ставим точку на нужном месте.
я у себя с датчиком HTU21D так и сделал.
например:
St = 0x6543 = 25923.
25923 * 17500 = 453652500.
сдвигаем 16 раз вправо и получаем целое число 6922.
теперь вычитаем 4500, и получаем 2422.
далее выводим 24, выводим точку и выводим 22.

для влажности делаем то же самое. в формуле для влажности (для твоей микры) берем не 100, а 10000, и получаем влажность в сотых долях процента.

Re: Как хранить double значения в int?

Пн дек 02, 2019 07:07:16

берем не 175, а 17500. и берем не 45, а 4500.

Это как раз то, что я искал. Спасибо)
Получается мы все числа в формуле умножили на 100, и сначала выполнили умножение, а уже потом деление чтоб при делении не получился ноль.

Но теперь меня заинтересовал формат числа с фиксированной точкой))

Re: Как хранить double значения в int?

Пн дек 02, 2019 15:40:32

для влажности делаем то же самое. в формуле для влажности (для твоей микры) берем не 100, а 10000
Но появляется необходимость в операции деления. В моём варианте деления нет.

Re: Как хранить double значения в int?

Пн дек 02, 2019 16:38:23

у меня тоже деления нет.
я уже выше сказал, что операция деления тут не нужна.
деление на 65536 производится отбрасыванием двух младших байтов или (то же самое) сдвигом вправо 16 раз.

Re: Как хранить double значения в int?

Пн дек 02, 2019 17:21:31

у меня тоже деления нет.
я уже выше сказал, что операция деления тут не нужна.
Нужна чтобы сотые доли градуса отделить от целых градусов при их передаче функции printf().
Ответить