如何转换4字节IEEE(little endian)浮点二进制表示

2024-09-22 16:22:46 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在解码一个二进制文件,它有四个字节表示的十进制数,小尾数。例如,94 53 F0 40表示7.510202。不幸的是,Python给了我7.51020240784。

当我试图使用unpack("<f",sampledata)[0]解析这些数据时,由于Python存储值的方式,我无法获得原始数据的精确表示(有关更多信息,请参见http://bugs.python.org/issue4114)。

不幸的是,我需要得到相同的表示形式,而不必考虑浮点数的不精确性,因为我需要将这些值写入文本文件,小数位数与最初写入二进制文件时的小数位数相同。

如果可能的话,我宁愿使用Python,但是如果需要的话,我很乐意用C实现一个解决方案。我不能简单地截断unpack函数的返回,原因是我不能保证原始float有多少个小数位,例如根据我的hex编辑器,0C 02 0F 41表示8.938,而原始二进制文件只有3个小数位。

为了清楚起见,我需要将四个十六进制字节作为输入,并输出一个文本/ASCII或IEEE 32位浮点数字的数字表示形式,该数字的小数位数与文件创建者预期的相同。我将使用输出创建原始二进制数据文件的CSV,而不是实际执行任何计算。

有什么建议吗?

示例:

from __future__ import print_function
from struct import *

print("Should print 7.510202")

hexbytes = b"\x94\x53\xF0\x40"

# 01101001 11001000 11110001 01000000
# should print 7.510202

print(unpack("<f",hexbytes)[0])

Tags: 文件fromimport字节二进制数字解码形式
3条回答

下面是一个如何用小尾数编码和解码的例子。这并没有解决任何舍入问题,但看起来这些问题是在上面的答案中解决的。

import csv, os
import struct

test_floats = [1.2, 0.377, 4.001, 5, -3.4]

## write test floats to a new csv file:
path_test_csv = os.path.abspath('data-test/test.csv')
print path_test_csv
test_csv = open(path_test_csv, 'w')
wr = csv.writer(test_csv)
for x in test_floats:
    wr.writerow([x])
test_csv.close()


## write test floats as binary
path_test_binary = os.path.abspath('data-test/test.binary')
test_binary = open(path_test_binary, 'w')
for x in test_floats:
    binary_data = struct.pack('<f', x)
    test_binary.write(binary_data)
test_binary.close()


## read in test binary
binary = open(path_test_binary, 'rb')
binary.seek(0,2) ## seeks to the end of the file (needed for getting number of bytes)
num_bytes = binary.tell() ## how many bytes are in this file is stored as num_bytes
# print num_bytes
binary.seek(0) ## seeks back to beginning of file
i = 0 ## index of bytes we are on
while i < num_bytes:
    binary_data = binary.read(4) ## reads in 4 bytes = 8 hex characters = 32-bits
    i += 4 ## we seeked ahead 4 bytes by reading them, so now increment index i
    unpacked = struct.unpack("<f", binary_data) ## <f denotes little endian float encoding
    print tuple(unpacked)[0]
  uint32_t b = 0x40F05394 + printf("");

  printf("%.11f\n", *(float *) &b);

在我的系统中打印:

7.51020240784

所以需要用f转换说明符打印更多的数字。与python一样,您只需请求要打印的位数。

示例:

print "%.11f" % (unpack("<f",hexbytes)[0])

如果要打印的位数在文本文件中是可变的,则还必须将此信息存储在文本文件中。

然后在C中可以打印:

      int p = 11;
      printf("%.*f\n", p, *(float *) &b);  // 11 here can be a variable

在Python中:

     p = 11
     print "%.*f" % (p, (unpack("<f",hexbytes)[0]))  # 11 can be a variable

当然,要从0x9453F040获得0x40F05394,只需要重新排列字节的顺序。

一个4字节的IEEE格式浮点数大约包含7位数字。您要做的是将unpack的结果舍入到总共7位。从那里,从float到string的普通Python转换将对您隐藏所有的浮点污点。

def magnitude(x):
    return 0 if x==0 else int(math.floor(math.log10(abs(x)))) + 1

def round_total_digits(x, digits=7):
    return round(x, digits - magnitude(x))

>>> round_total_digits(struct.unpack('<f', '\x94\x53\xF0\x40')[0])
7.510202
>>> round_total_digits(struct.unpack('<f', '\x0C\x02\x0F\x41')[0])
8.938
>>> x = struct.unpack('<f', struct.pack('<f', 12345.67))[0]
>>> x
12345.669921875
>>> round_total_digits(x)
12345.67

请注意,如果您的数字不是由十进制数直接转换而来,而是计算的结果,则这可能会降低总精度。但不是很多。

相关问题 更多 >