如何优雅地读/写结构体字段作为小端浮点数?

问题描述 投票:0回答:1

我编写了一些拦截数据包并修改它们的代码。数据包中的 4 个字节代表一个浮点值,但以相反的字节顺序(小端)。这给我带来了很多问题。让我用一段简化的代码来解释一下:

#include <Arduino.h>

typedef struct {
    uint8_t start_seq[2];
    uint8_t body_size;
    uint8_t type;
    uint8_t unknown;
    uint8_t entity_id;
    uint8_t reserved[3];
    float value;
    uint8_t end_seq[2];
} Packet;

void printRawPacket(const char* message, const uint8_t* packet, size_t length) {
    Serial.print(message);
    for (int i = 0; i < length; i++) {
        Serial.printf("%02X ", packet[i]);
    }
    Serial.println();
}

// Packet interceptor function
void handlePacket(uint8_t* raw_packet, size_t length) {
    // Create a pointer to the packet structure
    Packet* packet = (Packet*)raw_packet;

    printRawPacket("Original bytes: ", raw_packet, sizeof(raw_packet));
    // Correctly Prints: "Original bytes: BE EF 08 03 00 09 00 00 00 00 00 C0 40 EF BE"

    // Access the float value without creating a copy of the 4 bytes
    Serial.print("Original value: ");
    Serial.println(packet->value);
    // Incorrectly prints a random value or "ovf" instead of 6.00

    // Modify the float value without creating a copy of the 4 bytes
    packet->value = -120.0f; // little endian would be 00 00 F0 C2; big endian would be C2 F0 00 00
    Serial.print("Modified value: ");
    Serial.println(packet->value); // Prints -120.0 correctly

    printRawPacket("Modified bytes: ", raw_packet, sizeof(raw_packet));
    // Incorrectly Prints: "Modified bytes: BE EF 08 03 00 09 00 00 00 00 00 C0 00 00 F0"
    // Showing the float value was not stored correctly and the footer was overwritten
    // Expected output would have been: "Modified bytes: BE EF 08 03 00 09 00 00 00 00 00 00 00 F0 C2 EF BE"
}

void setup() {
    Serial.begin(115200);

    // Example packet
    uint8_t raw_packet[] = {
        // ------ HEADER ------
        0xBE, 0xEF, // start sequence
        0x08,       // body size uint8_t
        0x03,       // type uint8_t
        0x00,       // unknown
        // ------ HEADER ------

        // ------ BODY ------
        0x09,                   // entity id uint8_t
        0x00, 0x00, 0x00,       // reserved
        0x00, 0x00, 0xC0, 0x40, // value (float, little-endian) // 6.0f
        // ------ BODY ------

        // ------ FOOTER ------
        0xEF, 0xBE // end sequence
        // ------ FOOTER ------
    };

   handlePacket(raw_packet, sizeof(raw_packet));
}

void loop() {
    // Nothing to do here
}

正如评论中所解释的,这在多个级别上不起作用。

我可以通过将值字段设为

uint8_t value[4]
,然后使用如下所示的 getter 和 setter 函数来轻松解决该问题:

typedef struct {
    uint8_t start_seq[2];
    uint8_t body_size;
    uint8_t type;
    uint8_t unknown;
    uint8_t entity_id;
    uint8_t reserved[3];
    uint8_t value[4];
    uint8_t end_seq[2];

    // Function to get the float value from the raw packet in little-endian format
    float getFloatLeValue() const {
        // reverse byte order and return as float
        uint32_t temp = (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0];
        float result;
        memcpy(&result, &temp, sizeof(result)); // Copy uint32_t to float
        return result;
    }

    // Function to set the float value into the raw packet in little-endian format
    void setFloatLeValue(float newValue) {
        // Copy float to uint32_t
        uint32_t temp;
        memcpy(&temp, &newValue, sizeof(temp));
        // Copy uint32_t to value in little-endian format
        value[0] = temp & 0xFF;
        value[1] = (temp >> 8) & 0xFF;
        value[2] = (temp >> 16) & 0xFF;
        value[3] = (temp >> 24) & 0xFF;
    }
} Packet;

但是当我想做的只是反向解释 4 个字节时,这似乎显得不必要的复杂和复杂。

有没有更简单更优雅的方式?

(我正在使用 Arduino 框架在 ESP32 的 PlatformIO 上用 C++ 编写代码。)

c++ arduino floating-point endianness arduino-esp32
1个回答
0
投票

让我们看看@PasserBy 建议的

bit_cast
会是什么样子。我们可以使用来自 https://en.cppreference.com/w/cpp/numeric/bit_cast

的 memcpy 进行示例实现

我们还可以使用

htonl
/
ntohl
进行实际交换。或者还有
bswap_32

template<class To, class From>
To my_bit_cast(const From& src) noexcept
{
  To dst;
  memcpy(&dst, &src, sizeof(To));
  return dst;
}

float getFloatLeValue() const {
    return my_bit_cast<float, uint32_t>(
        ntohl(
            my_bit_cast<uint32_t, uint8_t[4]>(value)));
}

void setFloatLeValue(float newValue) {
    uint32_t temp = htonl(my_bit_cast<uint32_t, float>(newValue));
    memcpy(&value, &temp, sizeof(value));
}

呃...不太好?

© www.soinside.com 2019 - 2024. All rights reserved.