Как использовать python для преобразования числа с плавающей запятой в фиксированную точку с предопределенным количеством бит

У меня есть числа с плавающей запятой 32 (скажем, положительные числа) в формате numpy. Я хочу преобразовать их в числа с фиксированной точкой с предопределенным количеством бит, чтобы уменьшить точность.

Например, число 3,1415926 становится 3,25 в Matlab с помощью функции num2fixpt. Команда num2fixpt(3.1415926,sfix(5),2^(1 + 2-5), 'Nearest','on'), которая говорит 3 бита для целой части, 2 бита для дробной части.

Могу ли я сделать то же самое, используя Python


person tuming1990    schedule 08.05.2016    source источник
comment
Какова ваша конечная цель здесь?   -  person John Zwinck    schedule 08.05.2016
comment
я не хочу, чтобы число было слишком точным. Например, для имитации случая с использованием процессора цифровых сигналов с фиксированной точкой. Я хочу, чтобы число с плавающей запятой сохранялось в формате с фиксированной точкой с меньшей точностью.   -  person tuming1990    schedule 08.05.2016


Ответы (4)


Вы можете сделать это, если понимаете, как работает нотация с плавающей запятой IEEE. В основном вам нужно преобразовать в python LONG, выполнить побитовые операторы, а затем скрыть обратно. Например:

import time,struct,math
long2bits = lambda L: ("".join([str(int(1 << i & L > 0)) for i in range(64)]))[::-1]
double2long = lambda d: struct.unpack("Q",struct.pack("d",d))[0]
double2bits = lambda d: long2bits(double2long(d))
long2double = lambda L: struct.unpack('d',struct.pack('Q',L))[0]
bits2double = lambda b: long2double(bits2long(b))
bits2long=lambda z:sum([bool(z[i] == '1')*2**(len(z)-i-1) for i in range(len(z))[::-1]])

>>> pi = 3.1415926
>>> double2bits(pi)
'0100000000001001001000011111101101001101000100101101100001001010'
>>> bits2long('1111111111111111000000000000000000000000000000000000000000000000')
18446462598732840960L
>>> double2long(pi)
4614256656431372362
>>> long2double(double2long(pi) & 18446462598732840960L)
3.125
>>>

def rshift(x,n=1):
    while n > 0:
        x = 9223372036854775808L | (x >> 1)
        n -= 1
    return x

>>> L = bits2long('1'*12 + '0'*52)
>>> L
18442240474082181120L
>>> long2double(rshift(L,0) & double2long(pi))
2.0
>>> long2double(rshift(L,1) & double2long(pi))
3.0
>>> long2double(rshift(L,4) & double2long(pi))
3.125
>>> long2double(rshift(L,7) & double2long(pi))
3.140625

Это только урежет количество битов, но не округлит их. Функция rshift необходима, потому что оператор сдвига вправо в python заполняет пустой крайний левый бит нулем. См. описание IEEE с плавающей запятой здесь.

person Darin    schedule 08.05.2016

Вы можете округлить до двоичной фиксированной точности без явных преобразований типов, которые, как правило, генерируют много накладных расходов интерпретатора:

import numpy as np

n_bits = 2
f = (1 << n_bits)

a = np.linspace(1, 2, 11)
a_fix = np.round(a*f)*(1.0/f)

print a
print a_fix

Результаты в

[ 1.   1.1  1.2  1.3  1.4  1.5  1.6  1.7  1.8  1.9  2. ]
[ 1.    1.    1.25  1.25  1.5   1.5   1.5   1.75  1.75  2.    2.  ]

В примере используется numpy, но это просто для удобства создания списка значений примера. Встроенный в Python round будет работать так же хорошо для одиночных значений:

x=3.1415926
x_fix = round(x*f)/float(f)
print x_fix

Обратите внимание, что и f, и 1.0/f имеют точное представление с плавающей запятой; следовательно, умножение и деление выполняются точно, без ошибок округления. Также обратите внимание, что умножение на 1.0/f примерно в 3 раза быстрее, чем непосредственное деление в случае больших массивов.

Этот подход не контролирует количество битов для целочисленной части, поэтому, если вы хотите, чтобы числа были ограничены или перенесены, если они слишком велики, вам придется немного больше сдвигать биты.

person Han-Kwang Nienhuys    schedule 08.05.2016

Вы можете использовать модуль fxpmath для работы с дробной фиксированной точкой в ​​python.

Репозиторий модулей: https://github.com/francof2a/fxpmath

from fxpmath import Fxp

pi_fxp = Fxp(None, signed=False, n_word=5, n_frac=2)    # create fixed-point object (3 bit for intefer, 2 for fractional)
pi_fxp.rounding = 'around'                              # define rounding method, default is `trunc`.
pi_fxp(3.1415926)                                       # set value

print(pi_fxp)                                           # printed value = 3.25
person francof2a    schedule 25.04.2020

numfi — это библиотека Python, которая имитирует объект fi с фиксированной точкой Matlab, это больше похоже на fixdt в simulink. , но вы можете изменить num2fixpt на numfi, определив длину слова/дробной части вместо масштабирования

>>> from numfi import numfi
>>> x = numfi(3.1415926,1,5,2) # 5 word length = 2 fraction + 2 integer + 1 signed
>>> x
numfi([3.25]) s5/2-r/s
person ZinGer_KyoN    schedule 21.05.2021