Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: STM32F429: ошибка преобразования uint32_t в float
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Алексей ВМ
Добрый день,

STM32F429, Keil 5.16a

Возникла проблема при преобразовании uint32_t в float:

float * value;
uint32_t val = 0xFFFFFFFF;

*value = *(float*)&val;

В результате в *value -1.#QNAN. Что я делаю не так?

В тоже время float a = 0xFFFFFFFF; приводит к значениюю 4.2949673e+009 в переменной а.

Алексей.
Непомнящий Евгений
Это смотря чего вы хотите добиться. Преобразование чисел делается так
Код
float * value;
uint32_t val = 0xFFFFFFFF;

*value = val;


Вы же берете sizeof(float) байт по адресу &val и копируете их по адресу value.

ЗЫ На всякий случай - 4-байтный float число 0xffffffff точно сохранить не сможет, точность будет примерно 7 знаков ~4.2949672*10^9
Алексей ВМ
Цитата(Непомнящий Евгений @ Feb 3 2016, 16:20) *
Это смотря чего вы хотите добиться. Преобразование чисел делается так
Код
float * value;
uint32_t val = 0xFFFFFFFF;

*value = val;


Вы же берете sizeof(float) байт по адресу &val и копируете их по адресу value



Мне надо записать и прочитать float в еепром.

Есть функция

uint8_t EEPROM_Read(uint32_t Addr, uint8_t * buf, uint32_t count);

которая читает в uint8_t буфер байты. Если еепром чистая, то в буфере будут все 0xFF.

Функция, которая читает float, выглядит так

Код
uint8_t EEPROM_read_float(uint32_t Addr, float * value)
{    
        uint32_t val;
        uint8_t ret;        
    
        ret = EEPROM_Read(Addr, (uint8_t*)&val, 4);
    
        *value = *(float*)&val;

        return ret;    
}


Вот она и глючит.
Непомнящий Евгений
Цитата(Алексей ВМ @ Feb 3 2016, 16:27) *
Код
uint8_t EEPROM_read_float(uint32_t Addr, float * value)
{    
        return EEPROM_Read(Addr, (uint8_t*)&value, 4);    
}


Вот она и глючит.


Это не она глючит wink.gif
Алексей ВМ
Цитата(Непомнящий Евгений @ Feb 3 2016, 16:29) *
Это не она глючит wink.gif


ок, не она. А что (кто)?
Непомнящий Евгений
Цитата(Алексей ВМ @ Feb 3 2016, 16:31) *
ок, не она. А что (кто)?


я ж откуда знаю. У вас проблема в чем? Прочитанное не совпадает с записанным? Или то, что чистая eeprom дает вам NAN?
scifi
Цитата(Алексей ВМ @ Feb 3 2016, 16:27) *
Если еепром чистая, то в буфере будут все 0xFF.

ЕМНИП, float, в котором все байты 0xFF, - это неправильный float. Вот оно и ругается. Не все возможные коды допустимы для float.
Непомнящий Евгений
Цитата(scifi @ Feb 3 2016, 16:35) *
ЕМНИП, float, в котором все байты 0xFF, - это неправильный float. Вот оно и ругается. Не все возможные коды допустимы для float.


он не ругается, он говорит, что это NaN - Not a number. Это допустимое значение для float...

Вот тут можно поклацать и посмотреть http://www.h-schmidt.net/FloatConverter/IEEE754.html
Алексей ВМ
Цитата(scifi @ Feb 3 2016, 16:35) *
ЕМНИП, float, в котором все байты 0xFF, - это неправильный float. Вот оно и ругается. Не все возможные коды допустимы для float.


float a = 0xFFFFFFFF; приводит к значению 4.2949673e+009 в переменной а.









Цитата(Непомнящий Евгений @ Feb 3 2016, 16:33) *
У вас проблема в чем? Или то, что чистая eeprom дает вам NAN?


Именно. Причем для приведенного выше примера с присвоением в переменной число.
scifi
Цитата(Непомнящий Евгений @ Feb 3 2016, 16:39) *
он не ругается, он говорит, что это NaN - Not a number. Это допустимое значение для float...

Ну ладно, это вопрос интерпретации.
Но исходный вопрос как бы иллюстрирует кашу в голове у вопрошающего.. Там надо начинать с начала. Что хотим сделать? Что не получилось? Почему не получилось? Ну и т.д.
Непомнящий Евгений
Цитата(Алексей ВМ @ Feb 3 2016, 16:41) *
float a = 0xFFFFFFFF; приводит к значению 4.2949673e+009 в переменной а.

И что? Это же преобразование чисел по правилам арифметики, а не интерпретация 4-х байт как float

Цитата
Именно. Причем для приведенного выше примера с присвоением в переменной число.


Код
uint8_t EEPROM_read_float(uint32_t Addr, float * value)
{            
        ret = EEPROM_Read(Addr, (uint8_t*)&value, 4);
        if (*value != *value) // NaN != NaN
           *value = 0; // или что вы хотите по умолчанию?
        return ret;    
}



scifi
Цитата(Алексей ВМ @ Feb 3 2016, 16:41) *
float a = 0xFFFFFFFF; приводит к значению 4.2949673e+009 в переменной а.

Забавно, что Вас это удивляет.
Попробуйте понять, почему a и b будут иметь разные значения:
Код
float a = 0xFFFFFFFF;
const int i = 0xFFFFFFFF;
float b = *(float*)&i;

Это же азы. Нельзя же так...
Алексей ВМ
Цитата(Непомнящий Евгений @ Feb 3 2016, 16:44) *
И что? Это же преобразование чисел по правилам арифметики, а не интерпретация 4-х байт как float


ок, теперь ясно, спасибо.


Цитата(scifi @ Feb 3 2016, 16:45) *
Забавно, что Вас это удивляет.
Это же азы. Нельзя же так...


В переменной "а" не NaN, а число. Пример приведен лишь для этого, впрочем, выше уже все объяснили.
skripach
Цитата(Алексей ВМ @ Feb 3 2016, 16:27) *
Мне надо записать и прочитать float в еепром.

union в помощь. 1111493779.gif
Непомнящий Евгений
Цитата(skripach @ Feb 4 2016, 16:28) *
union в помощь. 1111493779.gif


Стесняюсь спросить, а зачем тут union?
skripach
Цитата(Непомнящий Евгений @ Feb 4 2016, 16:32) *
Стесняюсь спросить, а зачем тут union?

Не стесняйтесь, спрашивайте. ТС хочет писать в епром float, union поможет ему это сделать, я вот даже любезно подыскал исходничек.
Tarbal
Цитата(skripach @ Feb 4 2016, 17:53) *
Не стесняйтесь, спрашивайте. ТС хочет писать в епром float, union поможет ему это сделать, я вот даже любезно подыскал исходничек.


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

Поделюсь одним своим приемом.

Мне, пока не придумал как все возложить на компилятор, всегда приходилось строить таблицы адресов, по которым в EEPROM хранятся разные параметры.
Вот техника как избежать этого.

1. Объявляется тип структура, которая содержит все поля для записи в EEPROM.
2. Создается указатель типа этой структуры, который равен нулю.
3. Адрес поля структуры является адресом, по которому надо писать в EEPROM. Длина типа поля (или поля структуры) равна количеству байт для записи.

Все просто и легко модифицируемо. Функция записи в EEPROM не заморачивается с другими типами кроме однобайтовых.
Сергей Борщ
Цитата(Tarbal @ Feb 4 2016, 16:15) *
Кастингом тоже можно.
Это когда толпа теток в нижнем белье и без?
Непомнящий Евгений
Цитата(skripach @ Feb 4 2016, 16:53) *
Не стесняйтесь, спрашивайте. ТС хочет писать в епром float, union поможет ему это сделать, я вот даже любезно подыскал исходничек.


Оно конечно можно по всякому, но по вашей же ссылке

Цитата
The simplest way is just to treat it as a buffer.
Код
float x;
i2c.write(addr, (char*)&x, sizeof(x));
...
i2c.read(addr, (char*)&x, sizeof(x));
Tarbal
Цитата(Сергей Борщ @ Feb 4 2016, 18:21) *
Это когда толпа теток в нижнем белье и без?


В белье -- это не наш метод. Только без.
skripach
Цитата(Непомнящий Евгений @ Feb 4 2016, 17:36) *
Оно конечно можно по всякому, но по вашей же ссылке

и?
Очевидно ТС не силён в указателях, я предложил решение, на мой взгляд более правильное чем указатели.
Сергей Борщ
Цитата(Непомнящий Евгений @ Feb 4 2016, 16:36) *
но по вашей же ссылке
За приведение адреса двоичных (не текстовых!) данных к указателю на char бить линейкой по рукам. Есть uint8_t в stdint.h, для поборников чистоты C89 есть unsigned char.
Tarbal
Цитата(skripach @ Feb 4 2016, 19:19) *
и?
Очевидно ТС не силён в указателях, я предложил решение, на мой взгляд более правильное чем указатели.


Не более правильное, а альтернативное. Кастинг типов кстати используют чаще чем union в подобных случаях.
Сергей Борщ
Цитата(Tarbal @ Feb 4 2016, 17:55) *
Кастинг типов кстати используют
Вот объясните, почему кастинг используют, а приведение типов не юзают?
Tarbal
Цитата(Сергей Борщ @ Feb 4 2016, 19:47) *
Вот объясните, почему кастинг используют, а приведение типов не юзают?


Я все это больше 20 лет по-английски только называю. Так что не все термины помню. Извините если режет слух. Самое шедевральное из русского магазина в Америке:
"Вам сыр писом или послайсать?"
Непомнящий Евгений
Цитата(Сергей Борщ @ Feb 4 2016, 18:23) *
За приведение адреса двоичных (не текстовых!) данных к указателю на char бить линейкой по рукам. Есть uint8_t в stdint.h, для поборников чистоты C89 есть unsigned char.


По хорошему все подобные функции должны получать void*. Но почему-то многие пишут их так, что они получают char*. В Си по моему работает неявное приведение, а вот в плюсах приходится писать явно.

Цитата(skripach @ Feb 4 2016, 18:19) *
и?
Очевидно ТС не силён в указателях, я предложил решение, на мой взгляд более правильное чем указатели.


Ну если ТС хочет МК и Си, то с указателями таки надо бы разобраться... Ну и насчет правильности можно поспорить. Это же Си, тут операции с указателями - не грязный хак, а основа основ
Alechek
Цитата(Непомнящий Евгений @ Feb 5 2016, 10:11) *
По хорошему все подобные функции должны получать void*. Но почему-то многие пишут их так, что они получают char*. В Си по моему работает неявное приведение, а вот в плюсах приходится писать явно.

Именно! в пример - memcpy. Копирует что угодно и сколько угодно, но с размером в байтах. Сам когда-то на заре принмал входящие данные в char*. Давно отказался.
Всякие char, uint8_t и прочее однобайтовое - лишь чтобы статически заререзвировать буфер на этапе компиляции под пока неизвестные данные.
skripach
Цитата(Непомнящий Евгений @ Feb 5 2016, 08:11) *
Ну если ТС хочет МК и Си, то с указателями таки надо бы разобраться... Ну и насчет правильности можно поспорить.

Спорить не надо, потому что это только моё мнение и я не утверждаю что это абсолютная истина.
Цитата
на мой взгляд более правильное
scifi
Цитата(Tarbal @ Feb 4 2016, 20:23) *
Я все это больше 20 лет по-английски только называю.

Граммар наци негодуэ. Тогда уж "тайп каст". Или "кэст" - на мериканский манер. Или вообще "type cast" - буковки-то на клавиатуре присутствуют smile3009.gif
Tarbal
Цитата(scifi @ Feb 5 2016, 11:33) *
Граммар наци негодуэ. Тогда уж "тайп каст". Или "кэст" - на мериканский манер. Или вообще "type cast" - буковки-то на клавиатуре присутствуют smile3009.gif


Дык уже сказали как правильно. Я кстати учил по-русски, но это еще в СССР было. Забыл.
Сергей Борщ
Цитата(Непомнящий Евгений @ Feb 5 2016, 07:11) *
Но почему-то многие пишут их так, что они получают char*.
При этом автор такого исходника свято уверен, что это указатель на байт. Вот за это и бить. "Просто char" - это символ, и должен использоваться только для работы с символами. Для работы с байтами есть uint8_t или, в крайнем случае, unsigned char. Осознанное и правильное использование void * - это уже следующий уровень мастерства.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.