单精度大端浮点值转Python双精度浮点数(大端)

2 投票
4 回答
3048 浏览
提问于 2025-04-16 07:49

我需要从Arduino通过串口(RS-232)接收十六进制编码的单精度大端浮点值。怎么把它们转换成Python中的浮点数?Python的浮点数是大端的,而且是双精度的。

Arduino发送的内容像是“8192323E”,而我希望在Python中得到0.174387。我找到了一些关于如何“将十六进制转换为浮点数”的资料,但似乎都不适用于单精度浮点数。

从那个链接的页面来看,这个方法看起来不错:

from ctypes import *

def convert(s):
    i = int(s, 16)                   # convert from hex to a Python int
    cp = pointer(c_int(i))           # make this into a c integer
    fp = cast(cp, POINTER(c_float))  # cast the int pointer to a float pointer
    return fp.contents.value         # dereference the pointer, get the float

但是它仍然不能处理我的单精度浮点数。

在Java(Processing)中,我能够做到这一点:

float decodeFloat(String inString) {
  byte [] inData = new byte[4];

  inString = inString.substring(2, 10); // discard the leading "f:"
  inData[0] = (byte) unhex(inString.substring(0, 2));
  inData[1] = (byte) unhex(inString.substring(2, 4));
  inData[2] = (byte) unhex(inString.substring(4, 6));
  inData[3] = (byte) unhex(inString.substring(6, 8));

  int intbits = (inData[3] << 24) | ((inData[2] & 0xff) << 16) | ((inData[1] & 0xff) << 8) | (inData[0] & 0xff);
  //unhex(inString.substring(0, 8));
  return Float.intBitsToFloat(intbits);
}

供你参考,这段是运行在Arduino上的C代码,实现了十六进制编码。

void serialFloatPrint(float f) {
  byte * b = (byte *) &f;
  Serial.print("f:");
  for(int i=0; i<4; i++) {

    byte b1 = (b[i] >> 4) & 0x0f;
    byte b2 = (b[i] & 0x0f);

    char c1 = (b1 < 10) ? ('0' + b1) : 'A' + b1 - 10;
    char c2 = (b2 < 10) ? ('0' + b2) : 'A' + b2 - 10;

    Serial.print(c1);
    Serial.print(c2);
  }
}

4 个回答

0

看起来你提到的十六进制字符串里的数据顺序是先低位字节(也就是小端格式?)。至少你的Java代码是这样处理的,而你说它是有效的:

  inData[0] = (byte) unhex(inString.substring(0, 2));  // gets _first_ two bytes
  inData[1] = (byte) unhex(inString.substring(2, 4));  // and the next two
  inData[2] = (byte) unhex(inString.substring(4, 6));  // etc...
  inData[3] = (byte) unhex(inString.substring(6, 8));

  // this shifts the bytes at the end of the hex string more the the leading ones
  int intbits = (inData[3] << 24) | ((inData[2] & 0xff) << 16) | 
                ((inData[1] & 0xff) << 8) | (inData[0] & 0xff);

Python的int()函数在处理(十六进制或十进制)字符串时,期望高位字节在前面。所以你可以通过在convert()函数的开头加上类似的代码来解决这个问题:

def convert(s):
    s = ''.join(s[i:i+2] for i in range(8,-2,-2)) # put low byte pairs first
    i = int(s, 16)                   # convert from hex to a Python int
      ...

这样就能修复了。

更新

或者你也可以修正Arduino上运行的serialFloatPrint()函数,使其正确地进行小端格式的十六进制编码。

4

在编程中,有时候我们会遇到一些问题,比如代码运行不正常或者出现错误。这种时候,我们需要找到问题的原因并解决它。通常,我们可以通过查看错误信息来帮助我们理解发生了什么。

错误信息就像是程序给我们的提示,它告诉我们哪里出了问题。比如,它可能会说某个变量没有定义,或者某个函数没有找到。这些信息虽然有时候看起来很复杂,但其实它们是在告诉我们具体的错误位置和原因。

当我们看到这些错误信息时,可以尝试以下几个步骤来解决问题:

  • 首先,仔细阅读错误信息,找出提示的具体内容。
  • 然后,检查代码中提到的行,看看是否有拼写错误或者遗漏的部分。
  • 如果不明白错误信息的意思,可以在网上搜索相关的内容,通常会找到很多有用的解释和解决方案。
  • 最后,尝试修改代码并重新运行,看看问题是否解决。

记住,遇到问题是学习编程的一部分,不要气馁!每次解决问题都是一次进步的机会。

>>> struct.unpack('<f', '\x81\x92\x32\x3e')
(0.17438699305057526,)
5

基于Ignacio Vazquez-Abrams的回答

import binascii
import struct

text='8192323E'
print(struct.unpack('<f',binascii.unhexlify(text))[0])
# 0.17438699305057526

撰写回答