Как преобразовать двоичный файл (строку) в значение с плавающей запятой? - PullRequest
10 голосов
/ 06 января 2012

Я хочу преобразовать двоичное число в число с плавающей точкой. Вот пример возможности:

>>> float(-0b1110)

дает мне правильный вывод:

-14.0

К сожалению, я работаю с бинарными строками , т.е. мне нужно что-то вроде float('-0b1110').
Однако это не работает:

>>> float('-0b1110')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: invalid literal for float(): -0b1110

Я попытался использовать binascii.a2b_qp(string[, header]), который преобразует блок данных для печати в кавычки обратно в двоичные данные и возвращает двоичные данные. Но в итоге я получаю ту же ошибку:

>>> float(binascii.a2b_qp('-0b1110'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: invalid literal for float(): -0b1110

Я понимаю случаи, когда выходное число является целым числом, но что, если я хочу получить число 12,546? Как будет выглядеть вызов функции для двоичной строки?

Ответы [ 6 ]

13 голосов
/ 06 января 2012

В одном из ваших комментариев вы указали, что двоичное число представляет собой число с плавающей запятой в формате IEEE 754 binary64 длиной 8 байтов. Однако это не соответствует значению -0b1110, которое вы показали в качестве примера, поэтому я проигнорировал его и использовал свое собственное значение в правильном формате в качестве входных данных для проверки ответа, показанного ниже.

По сути, сначала выполняется преобразование двоичной строки в целочисленное значение, затем в строку необработанных байтов, которая передается в struct.unpack() для окончательного преобразования в значение с плавающей запятой. Функция bin_to_float(), показанная ниже, управляет процессом. Хотя это не показано, аргументы двоичной входной строки могут иметь префикс '0b'.

from codecs import decode
import struct


def bin_to_float(b):
    """ Convert binary string to a float. """
    bf = int_to_bytes(int(b, 2), 8)  # 8 bytes needed for IEEE 754 binary64.
    return struct.unpack('>d', bf)[0]


def int_to_bytes(n, length):  # Helper function
    """ Int/long to byte string.

        Python 3.2+ has a built-in int.to_bytes() method that could be used
        instead, but the following works in earlier versions including 2.x.
    """
    return decode('%%0%dx' % (length << 1) % n, 'hex')[-length:]


def float_to_bin(value):  # For testing.
    """ Convert float to 64-bit binary string. """
    [d] = struct.unpack(">Q", struct.pack(">d", value))
    return '{:064b}'.format(d)


if __name__ == '__main__':

    for f in 0.0, 1.0, -14.0, 12.546, 3.141593:
        print('Test value: %f' % f)
        binary = float_to_bin(f)
        print(' float_to_bin: %r' % binary)
        floating_point = bin_to_float(binary)  # Round trip.
        print(' bin_to_float: %f\n' % floating_point)

Выход:

Test value: 0.000000
 float_to_bin: '0000000000000000000000000000000000000000000000000000000000000000'
 bin_to_float: 0.000000

Test value: 1.000000
 float_to_bin: '0011111111110000000000000000000000000000000000000000000000000000'
 bin_to_float: 1.000000

Test value: -14.000000
 float_to_bin: '1100000000101100000000000000000000000000000000000000000000000000'
 bin_to_float: -14.000000

Test value: 12.546000
 float_to_bin: '0100000000101001000101111000110101001111110111110011101101100100'
 bin_to_float: 12.546000

Test value: 3.141593
 float_to_bin: '0100000000001001001000011111101110000010110000101011110101111111'
 bin_to_float: 3.141593
8 голосов
/ 06 января 2012

Другой вариант сделать

from ast import literal_eval

float_str = "-0b101010101"
result = float(literal_eval(float_str))

В отличие от встроенного «eval», literal_eval безопасен для запуска даже на пользовательских входах, так как он может анализировать только литералы Python - и не будет выполнять выражения, что означает, что он также не будет вызывать функции.

6 голосов
/ 06 января 2012
float(int('-0b1110',0))

Это работает для меня.

Если у вас есть 64-разрядная строка, представляющая число с плавающей запятой, а не целое число, вы можете выполнить трехступенчатое преобразование - первый шаг поворачиваетstring в целое число, вторая преобразует ее в 8-байтовую строку, а третья повторно интерпретирует эти биты как число с плавающей запятой.

>>> import struct
>>> s = '0b0100000000101001000101111000110101001111110111110011101101100100'
>>> q = int(s, 0)
>>> b8 = struct.pack('Q', q)
>>> struct.unpack('d', b8)[0]
12.546

Конечно, вы можете объединить все эти шаги в одну строку.

>>> s2 = '0b1100000000101100000000000000000000000000000000000000000000000000'
>>> struct.unpack('d', struct.pack('Q', int(s2, 0)))[0]
-14.0
2 голосов
/ 09 июля 2016

Это работает для меня.Протестировано с Python3.4:

def float_to_bin(num):
    return bin(struct.unpack('!I', struct.pack('!f', num))[0])[2:].zfill(32)

def bin_to_float(binary):
    return struct.unpack('!f',struct.pack('!I', int(binary, 2)))[0]

float_to_bin(bin_to_float(float_to_bin(123.123))) == float_to_bin(123.123)
>>> True
0 голосов
/ 06 января 2012

Вы можете преобразовать двоичное число в строковой форме в int, установив основание на 2 во встроенной функции int([x[, base]]), однако сначала вам нужно избавиться от 0b. Затем вы можете передать результат в float(), чтобы получить окончательный результат:

>>> s = '-0b1110'
>>> float(int(s.replace('0b', ''), 2))
-14.0

edit: Очевидно, что избавиться от 0b необходимо только на Python 2.5 и ниже, ответ Марка отлично работает для меня на Python 2.6, но вот что я вижу на Python 2.5:

>>> int('-0b1110', 2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 2: '-0b1110'
0 голосов
/ 06 января 2012

Вы можете использовать eval (''), а затем при необходимости разыграть его как float.Пример:

>> eval('-0b1110')
-14
>> float(eval('-0b1110'))
-14.0
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...