代码之家  ›  专栏  ›  技术社区  ›  Chris

如何将4字节IEEE(小端)浮点二进制表示转换为浮点

  •  5
  • Chris  · 技术社区  · 10 年前

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

    当我尝试使用 unpack("<f",sampledata)[0] 由于Python存储值的方式,我没有得到原始值的精确表示(有关详细信息,请参阅 http://bugs.python.org/issue4114 ).

    不幸的是,我 需要获得 准确的 相同的表示方式-不管关于浮点数不精确的讨论,因为我需要将这些值写入文本文件,小数位数与最初写入二进制文件时的小数位数相同。

    如果可能的话,我宁愿坚持使用Python,但如果有必要,我很乐意用C语言实现一个解决方案。我不能简单地截断解包函数的返回,原因是我不能保证原始浮点值有多少小数位数,例如 0C 02 0F 41 根据我的十六进制编辑器,代表8.938,来自原始二进制文件,只有3位小数。

    为了清楚起见,我需要将四个十六进制字节作为输入,并输出IEEE 32位浮点数字的文本/ASCII或数字表示,该数字表示的小数位数与文件创建者的预期相同。我将使用输出创建原始二进制数据文件的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])
    
    3 回复  |  直到 10 年前
        1
  •  5
  •   Mark Ransom    10 年前

    一个4字节的IEEE格式浮点数可以保存大约7位数字。你想做的是 unpack 总计7位。从那里开始,从浮点到字符串的正常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
    

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

        2
  •  1
  •   ouah    10 年前
      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
    

    当然有 0x40F05394 从…起 0x9453F040 ,您只需要重新排列字节的顺序。

        3
  •  0
  •   travelingbones    6 年前

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

    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]