Как перевести float в четыре байта?

float byte-order

Я пытаюсь сохранить GPS-координаты в EEPROM.

В этом примере я передаю ему широту 56060066 в качестве аргумента с плавающей запятой x

void writeFloat(unsigned int addr, float x)
{
  byte seriesOfBytes[3];
  *((float *)seriesOfBytes) = x;
  // Записываем все четыре байта.
  for(int i = 0; i < 4; i++)
  {
    i2c_eeprom_write_byte(0x57, addr, myFloat.bytes[i]); // Запись байта в EEPROM
    Serial.println(seriesOfBytes[i],BIN);  // Строка отладки
  }
}

Я ожидаю получить следующие четыре байта от серийной печати:

00000011
01010111
01101000
10100010

Вместо этого я получаю следующее:

00101000
11011010
01010101
01001100

Я пробовал менять всевозможные параметры, но не могу найти проблему. Кто-нибудь может определить проблему?

, 👍0


4 ответа


Лучший ответ:

1

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

По сути, вы пытаетесь преобразовать массив из четырех 8-битных значений, которые могут иметь любое выравнивание, которое им нравится (выравнивание по байтам), в 32-битное значение с плавающей запятой, для которого требуется выравнивание по 4 байтам. И они просто не сочетаются друг с другом.

Вместо этого вам нужно действовать наоборот — приводить тип с меньшими требованиями к выравниванию по сравнению с типом с более высокими требованиями.

Таким образом, вместо того, чтобы получать 4 байта и пытаться заполнить их как число с плавающей запятой, вы получаете число с плавающей запятой и читаете его как 4 байта:

byte *b = (byte *)&floatVal;

Тогда вы можете получить доступ от b[0] до b[3].

Еще один способ сделать это с объединением:

union {
    float fval;
    byte bval[4];
} floatAsBytes;

floatAsBytes.fval = floatVal;
EEPROM.write(0, floatAsBytes.bval[0]);
EEPROM.write(1, floatAsBytes.bval[1]);
EEPROM.write(2, floatAsBytes.bval[2]);
EEPROM.write(3, floatAsBytes.bval[3]);

Кроме того, в вашем коде вы выделили достаточно места только для 3 байтов, а не для 4...

,

Спасибо. Я только что попробовал союз, который вы предложили, но он показывает тот же результат! Компилятор не выдает никаких предупреждений. Ожидаете ли вы, что последовательная печать с параметром BIN покажет правильный результат?, @blarg

@blarg Думали ли вы, что, возможно, вы ожидаете неправильные значения? Откуда вы взяли эти значения?, @Majenko


2

Вы получаете именно то, что хотели. 4 байта, представляющие число в формате с плавающей запятой.

Вы ожидаете получить 4 байта, представляющих число в формате целого числа с обратным порядком байтов.

Используя решение Majenkos для приведения типов, вам нужно изменить типы и обратить цикл for для большей конечности.
Я также использовал int32_t, потому что размер <code>int</code> является 16-битным (2 байта) на Arduino Uno (и других платах на базе ATMega), а на Arduino Due — 32-битным (4 байта). В этом случае лучше определить точный int.

union {
    int32_t ival;
    byte bval[4];
} int32AsBytes;

void writeInt32(unsigned int addr, int32_t x)
{
    int32AsBytes.ival = x;
    for(int i = 3; i >= 0; i--) // обратный порядок байтов
    {
        i2c_eeprom_write_byte(0x57, addr, myFloat.bytes[i]); // Запись байта в EEPROM
        Serial.println(int32AsBytes.bval[i], BIN);  // Строка отладки
    }
}
,

1

Эта возня с приведениями и массивами слишком сложна.

См.: Чтение и запись структур данных в EEPROM

Для этого подойдет библиотека EEPROMAnything.h.


EEPROMAnything.h

Библиотека состоит только из этого файла. Сохраните в папку «библиотеки» под именем папки EEPROMAnything.

#include <Arduino.h>  // для определений типов
#include <EEPROM.h>

template <typename T> unsigned int EEPROM_writeAnything (int ee, const T& value)
{
    const byte* p = (const byte*)&value;
    unsigned int i;
    for (i = 0; i < sizeof(value); i++)
        EEPROM.write(ee++, *p++);
    return i;
}

template <typename T> unsigned int EEPROM_readAnything (int ee, T& value)
{
    byte* p = (byte*)&value;
    unsigned int i;
    for (i = 0; i < sizeof(value); i++)
        *p++ = EEPROM.read(ee++);
    return i;
}

Пример кода

#include <EEPROM.h>
#include <EEPROMAnything.h>

void setup ()
  {
  Serial.begin (115200);
  Serial.println ();
  float foo = 56060066;
  EEPROM_writeAnything (0, foo);

  float bar;
  EEPROM_readAnything (0, bar);

  Serial.println (bar);
  }  // end of setup

void loop ()
  {
  }  // end of loop

Вывод сверху

56060064.00

Обратите внимание, что вы не получите обратно точно такой же номер. Это связано с точностью 4-байтовых чисел с плавающей запятой.


После повторного прочтения вопроса я вижу, что вы используете I2C EEPROM. Это немного меняет ответ, но вы можете увидеть адаптацию этой библиотеки для I2C здесь. . Однако общая идея та же.


i2c_eeprom_write_byte(0x57, addr, myFloat.bytes[i]); // Запись байта в EEPROM

Похоже, ваш код записывает все байты на один и тот же адрес, я не знаю, как это может работать.

,

-1
union Float {
    float    m_float;
    uint8_t  m_bytes[sizeof(float)];
};

float        pi,pi1;
uint8_t      bytes[sizeof(float)];
uint8_t      bytes1[sizeof(float)];
Float        myFloat;


#include <Wire.h>    
#define disk1 0x50    //Адрес чипа 24LC256 eeprom
void setup(void)
{
  unsigned int address = 0;
  Serial.begin(9600);
  Wire.begin();
  pi = 300.78;
  Serial.println("*******************************************");
  Serial.println("pi = "+ String(pi));
  Serial.println("***** Conversion by using type casting *****");
  *(float*)(bytes) = pi; 
  for(int i=0;i<4;i++) Serial.println( bytes[i]);
  Serial.println("*******************************************");
  for( int i=0;i<4;i++){
      writeEEPROM(disk1, address, bytes[i]);
      address=address+sizeof(bytes[i]);
  }
   address=0;
   for( int i=0;i<4;i++){
      bytes1[i]=(readEEPROM(disk1, address));
      address++;
      Serial.println(bytes1[i]);
   }
   Serial.println("************Conver Byte to Float*************************");
 pi1 = *(float*)(bytes1); 
 Serial.println("pi = "+String( pi1)); 
 Serial.println("********* Conversion by using union *********");
 myFloat.m_float = pi1;   // присваиваем float объединению
 Serial.println("myFloat.m_Float = "+String( myFloat.m_float));
 Serial.println("myFloat.m_Bytes = "+String(myFloat.m_bytes[0])+String(myFloat.m_bytes[1])+String(myFloat.m_bytes[2])+String(myFloat.m_bytes[3]));  // получаем байты
}
void loop(){}
void writeEEPROM(int deviceaddress, unsigned int eeaddress, byte data ) 
{
  Wire.beginTransmission(deviceaddress);
  Wire.write((int)(eeaddress >> 8));   // старший бит
  Wire.write((int)(eeaddress & 0xFF)); // LSB Wire.send(data);
  Wire.write(data);
  Wire.endTransmission();
  delay(5);
}

byte readEEPROM(int deviceaddress, unsigned int eeaddress ) 
{
,

Надеюсь, это может помочь. Только что сделал несколько минут назад. удачи, @Nisit Wattanasri

Пожалуйста, добавьте несколько предложений, чтобы сопровождать код. Это может помочь тем, кто пытается понять это., @MichaelT