Я пытаюсь разбить переменную с плавающей запятой на байты, а затем повторно объединить с исходным float

Вот код, который я использую в настоящее время, было много различных альтернатив, которые я пробовал до сих пор безуспешно, это последнее воплощение, находящееся в моем основном цикле программы (пустой цикл)...

// получить температуру от датчика температуры Далласа
  sensors.requestTemperatures();
  sen0_data = sensors.getTempC(sensor_0_address);             // считываем датчик температуры0

// разделить float
  float f1 = sen0_data;
  byte* fb = (byte*) &f1;

  Serial.println(f1);
  Serial.println(fb[0]);    // Смотрим на байты
  Serial.println(fb[1]);
  Serial.println(fb[2]);
  Serial.println(fb[3]);

// перестроить float
  temp = (fb[0] << 24) + (fb[1] << 16) + (fb[2] << 8) + (fb[3]);
  Serial.println(temp);

Теперь результат, который можно увидеть в мониторе последовательного порта: исходное число с плавающей запятой, разделенный байт 0, байт 1, байт 2, байт 3 и, наконец, перестроенное число с плавающей запятой. Что я делаю неправильно? как, без сомнения, видно из грязного кодирования, я относительный новичок Начальное значение float -127,00 связано с тем, что в настоящее время у меня нет датчика температуры, подключенного к Arduino, но я все же ожидаю, что восстановленный float будет таким же, а не -318,00. Даже если я подключу датчик, восстановленный float не будет отражать оригинал...

-127.00
0
0
254
194
-318.00

Жду советов... С уважением, Саймон.

, 👍0

Обсуждение

AVR с прямым порядком байтов..., @Majenko


1 ответ


1

Мне сказали быть осторожным с простым преобразованием небайтовых переменных в массив байтов, потому что нет гарантии, что память, выделенная для определенных типов данных, будет непрерывной. Т.е. байты могут быть разбросаны по несмежным участкам памяти. Например, мне предложили использовать объединение для преобразования unit64_t, равного 8 байтам, в массив байтов на ESP8266. Это включает в себя объявление типа union:

union u64_bytes {
  uint64_t u64;
  uint8_t u8[sizeof(uint64_t)];
};

И затем вы можете использовать это для преобразования любым способом, например:

  uint64_t myNum = 1;
  u64_bytes splitNum;
  splitNum.u64 = myNum;
  // теперь я могу перебирать байты
  for(int i=0; i<sizeof(uint64_t); i++) {
      uint8_t myByte = splitNum.u8[i];
      Serial.print("byte ");
      Serial.print(i);
      Serial.print(" is ");
      Serial.println(myByte);
  }
  // или просто обратиться к отдельным байтам
  uint8_t oneByte = splitNum.u8[0];
  
  // вы также можете взять байты и преобразовать их в другую сторону
  u64_bytes rebuilt;
  for(int i=0; i<sizeof(splitNum); i++) {
      rebuilt.u8[i] = splitNum.u8[i];
  }
  
  Serial.print("The rebuilt value is ");
  Serial.println(rebuilt.u64);

EDIT: я добавил некоторый код, чтобы показать конверсию другим способом.

Тем не менее, в вашем случае, вероятно, довольно безопасно использовать побитовые операторы, которые вы используете. На вашем оборудовании число с плавающей запятой, по всей вероятности, составляет 4 байта, а в наши дни большая часть оборудования выделяет память блоками по четыре байта.

ОДНАКО вам все равно придется учитывать порядок байтов системы при сохранении значений. Основная идея состоит в том, что если у вас есть четыре байта, представляющих одно число, старшие значащие байты идут первыми или младшие значащие байты первыми?

Например, если вы говорите о четырехбайтовом беззнаковом целом числе 1, также известном как 0x00000001, оно будет храниться в шестнадцатеричном формате с обратным порядком байтов следующим образом:

0x00 0x00 0x00 0x01

Но на оборудовании с прямым порядком байтов (например, у меня) порядок другой. Они помещают в память младшие байты первыми следующим образом:

0x01 0x00 0x00 0x00

Это может сбивать с толку, но суть в том, что вам, возможно, придется дважды подумать о порядке байтов для ваших данных. Я бы добавил, что числа с плавающей запятой и целые числа со знаком могут иметь некоторые аспекты, которые вас удивят, и когда вы пытаетесь передать какую-либо информацию по последовательной линии (SPI, I2C и т. д.) или по сети, вам, возможно, придется беспокоиться о том, какой тип конверсий происходит. Вы должны убедиться, что знаете, в каком порядке находятся ваши байты, и что вы распаковываете их способом, соответствующим тому, как вы их упаковали.

Наконец, я бы посоветовал вам подумать (и, возможно, уточнить), чего вы надеетесь достичь, разбивая число с плавающей запятой на байты? Если вы не знаете о структуре битов внутри числа с плавающей запятой, вам нужно лучше ознакомиться с ней, прежде чем вы начнете возиться с байтами внутри него.

EDIT 2: вот пример, где я использую структуру для преобразования числа с плавающей запятой:

  union float_bytes {
    float flt;
    uint8_t u8[sizeof(float)];
  };

  float myNum = -127.0;
  float_bytes splitNum;
  splitNum.flt = myNum;
  // теперь я могу перебирать байты
  for(int i=0; i<sizeof(splitNum); i++) {
      uint8_t myByte = splitNum.u8[i];
      Serial.print("byte ");
      Serial.print(i);
      Serial.print(" is ");
      Serial.println(myByte);
  }
  // или просто обратиться к отдельным байтам
  uint8_t oneByte = splitNum.u8[0];
  
  // вы также можете взять байты и преобразовать их в другую сторону
  float_bytes rebuilt;
  for(int i=0; i<sizeof(splitNum); i++) {
      rebuilt.u8[i] = splitNum.u8[i];
  }
  
  Serial.print("The rebuilt value is ");
  Serial.println(rebuilt.flt);
,