\title{Floating Point (Q) format and Floating Point Rounding in myHDL} \author{Steven K Armour} \maketitle
In [1]:
from myhdl import *
from myhdlpeek import Peeker
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
from sympy import *
init_printing()
from bitstring import BitArray
#https://github.com/jrjohansson/version_information
%load_ext version_information
%version_information myhdl, myhdlpeek, numpy, pandas, matplotlib, sympy, bitstring
Out[1]:
In [2]:
#helper functions to read in the .v and .vhd generated files into python
def VerilogTextReader(loc, printresult=True):
with open(f'{loc}.v', 'r') as vText:
VerilogText=vText.read()
if printresult:
print(f'***Verilog modual from {loc}.v***\n\n', VerilogText)
return VerilogText
def VHDLTextReader(loc, printresult=True):
with open(f'{loc}.vhd', 'r') as vText:
VerilogText=vText.read()
if printresult:
print(f'***VHDL modual from {loc}.vhd***\n\n', VerilogText)
return VerilogText
In [3]:
#4 bit int, 4 bit float
Q=(4,4)
Qlen=Q[0]+Q[1]
Qscale=2**(Q[1]); Qscale
Out[3]:
In [4]:
a=3.6250; b=4.0625
c=a+b; c
Out[4]:
In [5]:
aQ=int(a*Qscale); bQ=int(b*Qscale)
f'aQ:{aQ}; bA:{bQ}'
Out[5]:
In [6]:
aQBV=intbv(aQ)[Qlen:]; bQBV=intbv(bQ)[Qlen:]
f'aQBV: {bin(aQBV, Qlen)}; bQBV: {bin(bQBV, Qlen)}'
Out[6]:
In [7]:
cQ=aQBV+bQBV; cQ
Out[7]:
In [8]:
c==cQ/Qscale
Out[8]:
In [9]:
class AddPosTVGen():
"""
Class to generate postive random numbers to be Qed for testing
"""
def __init__(self, Q, N):
"""
Take in arguments and create output holds
Args:
Q(tuple): Q notation tuple where Q[0] is int bit len and Q[1] is
dec bit len
N(int): number of values to generate
"""
self.Q=Q; self.N=N
self.Qlen=self.Q[0]+self.Q[1]; self.Qmax=2**self.Qlen
self.Qscale=2**self.Q[1]
self.aTV=np.zeros(0); self.aTVQ=np.zeros(0)
self.bTV=np.zeros(0); self.bTVQ=np.zeros(0)
self.cK=np.zeros(0); self.cKQ=np.zeros(0)
def Genrator(self):
"""
Random Number genrator in floating point and supsequent Qed version
"""
self.V1=np.array((1/np.random.ranf())).round(decimals=self.Q[1])
#needed to force np.random to generate a differint random num
np.random.seed(np.random.randint(self.Qmax))
self.V2=np.array((1/np.random.ranf())).round(decimals=self.Q[1])
self.V1Q=(self.V1*self.Qscale).astype(int)
self.V2Q=(self.V2*self.Qscale).astype(int)
def GenratorCheckAndAdd(self):
"""
Cheacks if the sum of the two randome numbers generated are going to break the Qmax
if they do dont append to retrun holds
"""
self.V1pV2=(self.V1+self.V2).round(decimals=self.Q[1])
self.V1pV2Q=(self.V1pV2*self.Qscale).astype(int)
if (self.V1Q+self.V2Q)<self.Qmax:
self.aTV=np.append(self.aTV, self.V1); self.aTVQ=np.append(self.aTVQ, self.V1Q).astype(int)
self.bTV=np.append(self.bTV, self.V1); self.bTVQ=np.append(self.bTVQ, self.V1Q).astype(int)
self.cK=np.append(self.cK, self.V1pV2); self.cKQ=np.append(self.cKQ, self.V1pV2Q).astype(int)
def MakeTVs(self):
"""
Automates the generating, testing and appending to make the TVs
Returns:
self.aTV(np.array): floating point numbers for a
self.aTVQ(np.array): fixed point Qed from self.aTV
self.bTV(np.array): floating point numbers for b
self.bTVQ(np.array): fixed point Qed from self.bTV
self.cK(np.array): known floating point rounded sum of self.aTV, self.bTV
self.cKQ(np.array): known fixed point Qed from self.cK
"""
while len(self.aTV)<=self.N:
self.Genrator()
self.GenratorCheckAndAdd()
#print('Done')
In [10]:
@block
def AdderBehaverial(a, b, c):
@always_comb
def logic():
c.next=a+b
return instances()
In [11]:
Peeker.clear()
a=Signal(intbv(0)[Qlen:]); Peeker(a, 'a')
b=Signal(intbv(0)[Qlen:]); Peeker(b, 'b')
c=Signal(intbv(0)[Qlen:]); Peeker(c, 'c')
TVG=AddPosTVGen(Q, 100); TVG.MakeTVs()
aTV=TVG.aTV; aTVQ=TVG.aTVQ
bTV=TVG.bTV; bTVQ=TVG.bTVQ
cKTV=TVG.cK; cKTVQ=TVG.cKQ
DUT=AdderBehaverial(a, b, c)
def Adder_TB():
@instance
def simules():
for i in range(len(aTVQ)):
a.next=int(aTVQ[i])
b.next=int(bTVQ[i])
yield delay(1)
raise StopSimulation()
return instances()
sim=Simulation(DUT, Adder_TB(), *Peeker.instances()).run()
In [12]:
Peeker.to_wavedrom()
In [13]:
PAOData=Peeker.to_dataframe()
#load in the source floating values
PAOData['aTV']=aTV; PAOData['bTV']=bTV
#get the predicted floating Point Sum
PAOData['aTV+bTV']=aTV+bTV
#get the predicted fixed point sum
PAOData['aQ+bQ']=aTVQ+bTVQ
#reorder
PAOData=PAOData[['a', 'aTV', 'b', 'bTV', 'aTV+bTV', 'aQ+bQ', 'c']]
#load the sourced Qed sum
PAOData['cKTVQ']=cKTVQ
#de Q the testbench gen sum
PAOData['cdQ']=PAOData['c']/Qscale
#load the sourced floting sum
PAOData['cKTV']=cKTV
PAOData
Out[13]:
In [14]:
#dataframe of error measures
PAODataErr=pd.DataFrame()
PAODataErr['aQ+bQ_c']=np.abs(PAOData['aQ+bQ']-PAOData['c'])
PAODataErr['c_cKTVQ']=np.abs(PAOData['c']-PAOData['cKTVQ'])
PAODataErr['cdQ_cKTV']=np.abs(PAOData['cdQ']-PAOData['cKTV'])
PAODataErr['c_cKTVQ__cdQ_cKTV']=np.abs((PAODataErr['c_cKTVQ']/ Qscale)- PAODataErr['cdQ_cKTV'])
PAODataErr
Out[14]:
In [15]:
PAODataErr.describe()
Out[15]:
In [16]:
DUT.convert()
VerilogTextReader('AdderBehaverial');
In [17]:
a=3.6250; aQ=int(a*Qscale);a, aQ
Out[17]:
In [18]:
b=-1.5; bMagQ=int(abs(b)*Qscale); bMagQ
Out[18]:
In [19]:
bMagQBV=bin(bMagQ, Qlen); bMagQBV
Out[19]:
In [20]:
bQBVComp="".join([str(int(not(int(i)))) for i in bMagQBV]); bQBVComp
Out[20]:
In [21]:
bQComp=int(bQBVComp, 2); bQComp
Out[21]:
In [22]:
bQ2Comp=bQComp+1; bQ2Comp
Out[22]:
In [23]:
bQBV2Comp=bin(bQ2Comp, 2); bQBV2Comp
Out[23]:
In [24]:
(BitArray(bin=bQBV2Comp).int)/ Qscale
Out[24]:
In [25]:
aQBV=intbv(aQ)[Qlen:].signed()
aQBV, bin(aQBV, Qlen), aQBV.min, aQBV.max
Out[25]:
In [26]:
bQBV=intbv(int(b*Qscale))[Qlen:].signed()
bQBV, bin(bQBV, Qlen)
Out[26]:
In [27]:
bQBV2Comp==bin(bQBV, Qlen)
Out[27]:
In [28]:
a+b
Out[28]:
In [29]:
c=aQBV+bQBV; c, c/Qscale
Out[29]:
In [30]:
class AddPosNegTVGen():
"""
Class to generate postive random numbers to be Qed for testing
"""
def __init__(self, Q, N):
"""
Take in arguments and create output holds
Args:
Q(tuple): Q notation tuple where Q[0] is int bit len and Q[1] is
dec bit len
N(int): number of values to generate
"""
self.Q=Q; self.N=N
self.Qlen=self.Q[0]+self.Q[1]
self.Qmin=-(2**(Qlen-1)); self.Qmax=2**(self.Qlen-1) -1
self.Qscale=2**self.Q[1]
self.aTV=np.zeros(0); self.aTVQ=np.zeros(0)
self.bTV=np.zeros(0); self.bTVQ=np.zeros(0)
self.cK=np.zeros(0); self.cKQ=np.zeros(0)
def Genrator(self):
"""
Random Number genrator in floating point and supsequent Qed version
"""
self.V1=np.array((1/np.random.ranf())).round(decimals=self.Q[1])
#needed to force np.random to generate a differint random num
np.random.seed(np.random.randint(self.Qmax))
self.V2=np.array((1/np.random.ranf())).round(decimals=self.Q[1])
#needed to force np.random to generate a differint random num
np.random.seed(np.random.randint(self.Qmax))
self.Sign=np.random.randint(2)
if self.Sign==1:
self.V2=-self.V2
self.V1Q=(self.V1*self.Qscale).astype(int)
self.V2Q=(self.V2*self.Qscale).astype(int)
def GenratorCheckAndAdd(self):
"""
Cheacks if the sum of the two randome numbers generated are going to break the Qmax
if they do dont append to retrun holds
"""
self.V1pV2=(self.V1+self.V2).round(decimals=self.Q[1])
self.V1pV2Q=(self.V1pV2*self.Qscale).astype(int)
check=self.V1Q+self.V2Q
if self.V1Q>self.Qmin and self.V1Q<self.Qmax:
if self.V2Q>self.Qmin and self.V2Q<self.Qmax:
if check>self.Qmin and check<self.Qmax:
self.aTV=np.append(self.aTV, self.V1); self.aTVQ=np.append(self.aTVQ, self.V1Q).astype(int)
self.bTV=np.append(self.bTV, self.V2); self.bTVQ=np.append(self.bTVQ, self.V1Q).astype(int)
self.cK=np.append(self.cK, self.V1pV2); self.cKQ=np.append(self.cKQ, self.V1pV2Q).astype(int)
def MakeTVs(self):
"""
Automates the generating, testing and appending to make the TVs
Returns:
self.aTV(np.array): floating point numbers for a
self.aTVQ(np.array): fixed point Qed from self.aTV
self.bTV(np.array): floating point numbers for b
self.bTVQ(np.array): fixed point Qed from self.bTV
self.cK(np.array): known floating point rounded sum of self.aTV, self.bTV
self.cKQ(np.array): known fixed point Qed from self.cK
"""
while len(self.aTV)<=self.N:
self.Genrator()
self.GenratorCheckAndAdd()
#print('Done')
In [31]:
Peeker.clear()
a=Signal(intbv(0)[Qlen:].signed()); Peeker(a, 'a')
b=Signal(intbv(0)[Qlen:].signed()); Peeker(b, 'b')
c=Signal(intbv(0)[Qlen:].signed()); Peeker(c, 'c')
TVG=AddPosNegTVGen(Q, 100); TVG.MakeTVs()
aTV=TVG.aTV; aTVQ=TVG.aTVQ
bTV=TVG.bTV; bTVQ=TVG.bTVQ
cKTV=TVG.cK; cKTVQ=TVG.cKQ
DUT=AdderBehaverial(a, b, c)
def Adder_TB():
@instance
def simules():
for i in range(len(aTVQ)):
a.next=int(aTVQ[i])
b.next=int(bTVQ[i])
yield delay(1)
raise StopSimulation()
return instances()
sim=Simulation(DUT, Adder_TB(), *Peeker.instances()).run()
In [32]:
Peeker.to_wavedrom()
In [33]:
AOData=Peeker.to_dataframe()
#load in the source floating values
AOData['aTV']=aTV; AOData['bTV']=bTV
#get the predicted floating Point Sum
AOData['aTV+bTV']=aTV+bTV
#get the predicted fixed point sum
AOData['aQ+bQ']=aTVQ+bTVQ
#reorder
AOData=AOData[['a', 'aTV', 'b', 'bTV', 'aTV+bTV', 'aQ+bQ', 'c']]
#load the sourced Qed sum
AOData['cKTVQ']=cKTVQ
#de Q the testbench gen sum
AOData['cdQ']=AOData['c']/Qscale
#load the sourced floting sum
AOData['cKTV']=cKTV
AOData
Out[33]:
In [34]:
#dataframe of error measures
AODataErr=pd.DataFrame()
AODataErr['aQ+bQ_c']=np.abs(AOData['aQ+bQ']-AOData['c'])
AODataErr['c_cKTVQ']=np.abs(AOData['c']-AOData['cKTVQ'])
AODataErr['cdQ_cKTV']=np.abs(AOData['cdQ']-AOData['cKTV'])
AODataErr['c_cKTVQ__cdQ_cKTV']=np.abs((AODataErr['c_cKTVQ']/ Qscale)- AODataErr['cdQ_cKTV'])
AODataErr
Out[34]:
In [35]:
AODataErr.describe()
Out[35]:
In [36]:
DUT.convert()
VerilogTextReader('AdderBehaverial');
In [37]:
#Q4.4 *Q4.4 -> Q8.8
Q2=(Q[0]*2, Q[1]*2)
Q2len=Q2[0]+Q2[1]
Q2scale=2**(Q2[1]); Q2scale
Out[37]:
In [38]:
a=3.2500; aQ=int(a*Qscale)
b=-2.065; bQ=int(b*Qscale)
aQ, bQ
bin(aQ, Qlen), bin(bQ, Qlen)
Out[38]:
In [39]:
ab=a*b; ab
abQ=int(ab*Qscale); abQ
abdQ=abQ/ Qscale; abdQ, ab
Out[39]:
In [40]:
aQBV=intbv(aQ)[Qlen:].signed(); bQBV=intbv(bQ)[Qlen:].signed()
f'aQBV: {bin(aQBV, Qlen)}; bQBV: {bin(bQBV, Qlen)}'
Out[40]:
In [41]:
abQ=aQBV*bQBV; abQ
Out[41]:
In [42]:
abdQ=abQ/ Qscale; abdQ, ab
Out[42]:
In [43]:
abdQ=abQ/ Q2scale; abdQ,ab
Out[43]:
In [44]:
class MultPosNegTVGen():
"""
Class to generate postive random numbers to be Qed for testing
"""
def __init__(self, Q, N):
"""
Take in arguments and create output holds
Args:
Q(tuple): Q notation tuple where Q[0] is int bit len and Q[1] is
dec bit len
N(int): number of values to generate
"""
self.Q=Q; self.N=N
self.Qlen=self.Q[0]+self.Q[1]
self.Qmin=-(2**(self.Qlen-1)); self.Qmax=2**(self.Qlen-1) -1
self.Qscale=2**self.Q[1]
#Q4.4 *Q4.4 -> Q8.8
self.Q2=(self.Q[0]*2, self.Q[1]*2)
self.Q2len=self.Q2[0]+self.Q2[1]
self.Q2min=-(2**(self.Q2len-1)); self.Q2max=2**(self.Q2len-1) -1
self.Q2scale=2**(Q2[1])
self.aTV=np.zeros(0); self.aTVQ=np.zeros(0)
self.bTV=np.zeros(0); self.bTVQ=np.zeros(0)
self.cK=np.zeros(0); self.cKQ=np.zeros(0)
def Genrator(self):
"""
Random Number genrator in floating point and supsequent Qed version
"""
self.V1=np.array((1/np.random.ranf())).round(decimals=self.Q[1])
#needed to force np.random to generate a differint random num
np.random.seed(np.random.randint(self.Qmax))
self.V2=np.array((1/np.random.ranf())).round(decimals=self.Q[1])
#needed to force np.random to generate a differint random num
np.random.seed(np.random.randint(self.Qmax))
self.Sign=np.random.randint(2)
if self.Sign==1:
self.V2=-self.V2
self.V1Q=(self.V1*self.Qscale).astype(int)
self.V2Q=(self.V2*self.Qscale).astype(int)
def GenratorCheckAndMul(self):
"""
Cheacks if the sum of the two randome numbers generated are going to break the Qmax
if they do dont append to retrun holds
"""
self.V1tV2=(self.V1*self.V2).round(decimals=self.Q2[1])
self.V1tV2Q=(self.V1tV2*self.Q2scale).astype(int)
check=self.V1Q*self.V2Q
if self.V1Q>self.Qmin and self.V1Q<self.Qmax:
if self.V2Q>self.Qmin and self.V2Q<self.Qmax:
if check>self.Q2min and check<self.Q2max:
self.aTV=np.append(self.aTV, self.V1); self.aTVQ=np.append(self.aTVQ, self.V1Q).astype(int)
self.bTV=np.append(self.bTV, self.V2); self.bTVQ=np.append(self.bTVQ, self.V2Q).astype(int)
self.cK=np.append(self.cK, self.V1tV2); self.cKQ=np.append(self.cKQ, self.V1tV2Q).astype(int)
def MakeTVs(self):
"""
Automates the generating, testing and appending to make the TVs
Returns:
self.aTV(np.array): floating point numbers for a
self.aTVQ(np.array): fixed point Qed from self.aTV
self.bTV(np.array): floating point numbers for b
self.bTVQ(np.array): fixed point Qed from self.bTV
self.cK(np.array): known floating point rounded sum of self.aTV, self.bTV
self.cKQ(np.array): known fixed point Qed from self.cK
"""
while len(self.aTV)<=self.N:
self.Genrator()
self.GenratorCheckAndMul()
#print('Done')
In [45]:
@block
def MultiBehaverial(a, b, c):
@always_comb
def logic():
c.next=a*b
return instances()
In [46]:
Peeker.clear()
a=Signal(intbv(0)[Qlen:].signed()); Peeker(a, 'a')
b=Signal(intbv(0)[Qlen:].signed()); Peeker(b, 'b')
c=Signal(intbv(0)[Q2len:].signed()); Peeker(c, 'c')
TVG=MultPosNegTVGen(Q, 100); TVG.MakeTVs()
aTV=TVG.aTV; aTVQ=TVG.aTVQ
bTV=TVG.bTV; bTVQ=TVG.bTVQ
cKTV=TVG.cK; cKTVQ=TVG.cKQ
DUT=MultiBehaverial(a, b, c)
def Multi_TB():
@instance
def simules():
for i in range(len(aTVQ)):
a.next=int(aTVQ[i])
b.next=int(bTVQ[i])
yield delay(1)
raise StopSimulation()
return instances()
sim=Simulation(DUT, Multi_TB(), *Peeker.instances()).run()
In [47]:
Peeker.to_wavedrom()
In [48]:
MultiData=Peeker.to_dataframe()
#load in the source floating values
MultiData['aTV']=aTV; MultiData['bTV']=bTV
#get the predicted floating Point Sum
MultiData['aTV*bTV']=aTV*bTV
#get the predicted fixed point sum
MultiData['aQ*bQ']=aTVQ*bTVQ
#reorder
MultiData=MultiData[['a', 'aTV', 'b', 'bTV', 'aTV*bTV', 'aQ*bQ', 'c']]
#load the sourced Qed sum
MultiData['cKTVQ']=cKTVQ
#de Q the testbench gen sum
MultiData['cdQ']=MultiData['c']/Q2scale
#load the sourced floting sum
MultiData['cKTV']=cKTV
MultiData
Out[48]:
In [49]:
#dataframe of error measures
MultiDataErr=pd.DataFrame()
MultiDataErr['aQ*bQ_c']=np.abs(MultiData['aQ*bQ']-MultiData['c'])
MultiDataErr['c_cKTVQ']=np.abs(MultiData['c']-MultiData['cKTVQ'])
MultiDataErr['cdQ_cKTV']=np.abs(MultiData['cdQ']-MultiData['cKTV'])
MultiDataErr['c_cKTVQ__cdQ_cKTV']=np.abs((MultiDataErr['c_cKTVQ']/ Q2scale)- MultiDataErr['cdQ_cKTV'])
MultiDataErr
Out[49]:
In [50]:
MultiDataErr.describe()
Out[50]:
In [51]:
DUT.convert()
VerilogTextReader('MultiBehaverial');
In [52]:
#Q4.4 *Q4.4 -> Q8.8
Q2=(Q[0]*2, Q[1]*2)
Q2len=Q2[0]+Q2[1]
Q2scale=2**(Q2[1]); Q2scale
Out[52]:
In [79]:
a=3.2500; aQ=int(a*Qscale)
b=2.0625; bQ=int(b*Qscale)
aQ, bQ
#bin(aQ, Qlen), bin(bQ, Qlen)
Out[79]:
In [54]:
ab=a*b; ab
abQ=int(ab*Qscale); abQ
abdQ=abQ/ Qscale; abdQ, ab
Out[54]:
In [55]:
aQBV=intbv(aQ)[Qlen:]; bQBV=intbv(bQ)[Qlen:]
f'aQBV: {bin(aQBV, Qlen)}; bQBV: {bin(bQBV, Qlen)}'
Out[55]:
In [56]:
abQ=aQBV*bQBV; abQ
Out[56]:
In [57]:
abQBV=intbv(abQ)[Q2len:].signed(); abQBV, bin(abQBV), len(bin(abQBV))
Out[57]:
In [58]:
for j in range(Q2[1]):
Trunc=abQBV[Q2len:j]
TruncDQ=Trunc/(2**(Q2[1]-j))
print(bin(Trunc), TruncDQ, np.abs(ab-TruncDQ))
In [78]:
a=3.2500; aQ=int(a*Qscale)
b=-2.0625; bQ=int(b*Qscale)
aQ, bQ
#bin(aQ, Qlen), bin(bQ, Qlen)
Out[78]:
In [60]:
ab=a*b; ab
abQ=int(ab*Qscale); abQ
abdQ=abQ/ Qscale; abdQ, ab
Out[60]:
In [61]:
aQBV=intbv(aQ)[Qlen:].signed(); bQBV=intbv(bQ)[Qlen:].signed()
f'aQBV: {bin(aQBV, Qlen)}; bQBV: {bin(bQBV, Qlen)}'
Out[61]:
In [62]:
abQ=aQBV*bQBV; abQ
Out[62]:
In [63]:
abQBV=intbv(abQ)[Q2len:].signed(); abQBV, bin(abQBV), len(bin(abQBV))
Out[63]:
In [64]:
for j in range(Q2[1]):
Trunc=abQBV[Q2len:j].signed()
TruncDQ=Trunc/(2**(Q2[1]-j))
print(bin(Trunc), TruncDQ, np.abs(ab-TruncDQ))
In [65]:
for j in range(Q2[1]):
Trunc=(abQBV>>j).signed()
TruncDQ=Trunc/(2**(Q2[1]-j))
print(bin(Trunc), TruncDQ, np.abs(ab-TruncDQ))
In [66]:
a=3.2500; aQ=int(a*Qscale)
b=-2.0625; bQ=int(b*Qscale)
aQ, bQ
bin(aQ, Qlen), bin(bQ, Qlen)
Out[66]:
In [67]:
ab=a*b; ab
abQ=int(ab*Qscale); abQ
abdQ=abQ/ Qscale; abdQ, ab
Out[67]:
In [68]:
aQBV=intbv(aQ)[Qlen:].signed(); bQBV=intbv(bQ)[Qlen:].signed()
f'aQBV: {bin(aQBV, Qlen)}; bQBV: {bin(bQBV, Qlen)}'
Out[68]:
In [69]:
abQ=aQBV*bQBV; abQ
Out[69]:
In [70]:
abQBV=intbv(abQ)[Q2len:].signed(); abQBV, bin(abQBV), len(bin(abQBV))
Out[70]:
In [71]:
ab, floor(ab+.5), -ceiling(-ab-.5), ceiling(floor(2*ab)/2)
Out[71]:
In [72]:
Round=abQBV[Q2len-1:0].signed()
RoundDQ=Round/(2**(Q2[1]))
print(bin(Round), RoundDQ, np.abs(ab-RoundDQ))
{ {(OWID){1'b0}}, 1'b1, {(IWID-OWID-1){1'b0}} }
, .5
i_data[(IWID-1):0]+ { {(OWID){1'b0}}, 1'b1, {(IWID-OWID-1){1'b0}} }
, x+.5
w_halfup[(IWID-1):(IWID-OWID)]
, floor(x+.5)
In [73]:
concat(intbv(0)[8:], True, intbv(0)[16-8-1:])
Out[73]:
In [74]:
PointFive=intbv(int(.5*Q2scale))[16:]; PointFive, bin(PointFive, 16)
Out[74]:
In [75]:
abQBVP5=intbv(abQBV+PointFive)[16:].signed()
abQBVP5
Out[75]:
In [76]:
abQBVP5=abQBVP5[Q2len-1:Q2len-Qlen].signed(); abQBVP5
Out[76]:
In [77]:
abQBVP5, floor(ab+.5)
Out[77]:
In [ ]:
In [ ]: