【问题标题】:Float array to bytes converts as null浮点数组到字节转换为空
【发布时间】:2019-03-26 08:27:40
【问题描述】:

我正在尝试通过 MQTT 协议连续发送 6 个浮点数的数组。我使用 sprintf 函数将它们作为 ASCII 字符发送。我决定将它们作为原始字节发送。我将这些浮点数放入一个联合中以将它们表示为无符号字符。问题是当这些浮点数中的任何一个是整数值时,它们的字节表示在整数位置之后变为空。

union {
    float array[6];
    unsigned char bytes[6 * sizeof(float)];
} floatAsBytes;

如果所有 floatAsBytes.array 都包含浮点值,则完全没有问题。

如果我说floatAsBytes.array[0] = 0,floatAsBytes.bytes 将变为 null。

如果我说floatAsBytes.array[3] = 4,我可以看到前 8 个字节,但是这次最后 16 个字节变为空。

我的客户端 C 代码的最小示例

#define QOS 0
#define TIMEOUT     1000L

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <limits.h>
#include "MQTTClient.h"

union bitFloat{
    float f[6];
    unsigned char s[6*sizeof(float)];
};

void publish(MQTTClient client, char* topic, char* payload) {
    MQTTClient_message pubmsg = MQTTClient_message_initializer;
    pubmsg.payload = payload;
    pubmsg.payloadlen = strlen(pubmsg.payload);
    pubmsg.qos = QOS;
    pubmsg.retained = 0;
    MQTTClient_deliveryToken token;
    MQTTClient_publishMessage(client, topic, &pubmsg, &token);
    MQTTClient_waitForCompletion(client, token, TIMEOUT);
}

int main(){

    MQTTClient client;
    MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
    MQTTClient_message pubmsg = MQTTClient_message_initializer;
    MQTTClient_deliveryToken token;
    int rc;

    MQTTClient_create(&client, "MQTTADDRESS:MQTTPORT", "TestClient",
                        MQTTCLIENT_PERSISTENCE_NONE, NULL);
    conn_opts.keepAliveInterval = 20;
    conn_opts.cleansession = 1;

    if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS)
    {
        printf("Failed to connect, return code %d\n", rc);
        exit(-1);
    }

    int i;

    while(1){
        union bitFloat payload;
        payload.f[0] = 4.53; payload.f[1] = 2.39; payload.f[2] = 28.96; payload.f[3] = -1.83; payload.f[4] = -27.0; payload.f[5] = 9.32;
        publish(client, "MyTestTopic", payload.s);
        sleep(1);
    }
    return 0;
}

接收消息并显示消息的 Python 脚本

# !/usr/bin/env python

import struct
import numpy as np
import paho.mqtt.client as mqtt

def on_message(client, userdata, message):
    test1 = struct.unpack('<f', message.payload[0:4])[0]
    test2 = struct.unpack('<f', message.payload[4:8])[0]
    test3 = struct.unpack('<f', message.payload[8:12])[0]
    test4 = struct.unpack('<f', message.payload[12:16])[0]
    test5 = struct.unpack('<f', message.payload[16:20])[0]
    test6 = struct.unpack('<f', message.payload[20:24])[0]
    print(test1, test2, test3, test4, test5, test6)

client = mqtt.Client()
client.on_message = on_message

client.connect("MQTTADDRESS", MQTTPORT)
client.subscribe("MyTestTopic")

client.loop_forever()

【问题讨论】:

  • 这可能与字节序或网络字节顺序有关。检查你得到了哪些字节,你发送了哪些字节,以及两端使用了哪种字节序。
  • 字节顺序为小端。同样在发送它们之前,我正在使用 printf("Byte representation: %s\n", floatAsBytes.bytes) 检查它。它在客户端和服务器端都是空的。 @RudyVelthuis
  • 好的,那不是问题。
  • 您能否提供一个发生意外行为的测试样本?

标签: c floating-point byte bit


【解决方案1】:

这一行

pubmsg.payloadlen = strlen(pubmsg.payload);

错了。您在不是字符串的东西上使用strlen。由于使用了strlen,长度将是错误的,因为strlen 只计数直到它看到一个为零的字节。

示例: 考虑payload.f[0] = 1;。 1.0的二进制表示是3f800000

在小端系统上,这将被保存为 00 00 80 3f,因此使用 strlen 将导致 0。

在大端系统上,这将被保存为 3f 80 00 00,因此使用 strlen 将得到 2。

换句话说 - strlen 是错误的函数。

你可能需要

pubmsg.payloadlen = 6 * sizeof(float);

【讨论】:

    【解决方案2】:

    代码按预期工作。

    这是Minimal Complete Verifiable Example。我猜你正在做正确的事情。当您提供自己的代码时,可能表明我误解了您的问题:

    #include <stdio.h>
    
    int main()
    {
    
      union {
          float array[6];
          unsigned char bytes[6 * sizeof(float)];
      } floatAsBytes;
    
      // load up array with some date
      for(int i = 0; i < 6; i++) {
        floatAsBytes.array[i] =  1.99 + i;
      }
    
      puts("\nfirst run:");
    
      floatAsBytes.array[0] = 0;
    
      // dump array
      for(int i = 0; i< 6; i++) {
        printf("float #%d: %f\n", i, floatAsBytes.array[i]);
    
      }
    
      // dump bytes
      for(int i = 0; i < sizeof(float)*6; i++) {
        if(i % sizeof(float) == 0)
          printf("\n");
        printf(" %2x",floatAsBytes.bytes[i]);
      }
    
      // second example
      puts("\nSecond run:");
    
      floatAsBytes.array[3] = 4;
      // dump array
      for(int i = 0; i< 6; i++) {
        printf("float #%d: %f\n", i, floatAsBytes.array[i]);
    
      }
    
      // dump bytes
      for(int i = 0; i < sizeof(float)*6; i++) {
        if(i % sizeof(float) == 0)
          printf("\n");
        printf(" %2x",floatAsBytes.bytes[i]);
      }
      return 0;
    }
    

    这是输出:

    first run:
    float #0: 0.000000
    float #1: 2.990000
    float #2: 3.990000
    float #3: 4.990000
    float #4: 5.990000
    float #5: 6.990000
    
      0  0  0  0
     29 5c 3f 40
     29 5c 7f 40
     14 ae 9f 40
     14 ae bf 40
     14 ae df 40
    Second run:
    float #0: 0.000000
    float #1: 2.990000
    float #2: 3.990000
    float #3: 4.000000
    float #4: 5.990000
    float #5: 6.990000
    
      0  0  0  0
     29 5c 3f 40
     29 5c 7f 40
      0  0 80 40
     14 ae bf 40
     14 ae df 40
    Process finished with exit code 0
    

    我没有看到您所描述的行为。代码按预期工作。

    【讨论】:

    • 没有。 OP从未提供他的代码。我认为代码可以清楚地说明这个问题。我提供了我对他的代码的理解。他的声明,“问题是当这些浮点数中的任何一个是整数值时,它们的字节表示在整数位置之后变为空。”现在更容易理解了。
    • 我的意思是:你不知道他的代码,但你发现你的代码运行良好。这很酷,但是由于您不知道他真正在做什么,因此您的代码运行良好这一事实并不那么重要,IMO。
    • 我在一定程度上同意你的看法。但是,在许多情况下,我看到的问题根本没有 mcve。如果OP将其发布,则问题将得到解决。作为一个问题回答者,我可以提出经常得不到回答的查询,或者我可以向 OP 说明 MVCE 的外观。通过这样做,我可以教育 OP 披露真正的问题,或者我可以解决问题。我冒着不完全一致的答案的风险来承受打击,但我认为这通常会有所帮助。请参阅我的答案介绍。
    猜你喜欢
    • 2014-02-22
    • 1970-01-01
    • 1970-01-01
    • 2016-08-21
    • 2017-03-25
    • 2010-09-05
    • 1970-01-01
    相关资源
    最近更新 更多