each number is represented by a 32 bit integer. the fixedpoint position is not explicitly stored. ( as with floating point numbers )
r(A,nA) = A*2^-nA
here 'A' is the integer representation of real number r(A,nA) with 'nA' fractional bits.
in the next sections I assume both numbers operated upon have different fractional parts.
fixedpoint numbers are represented as a hash, with 2 elements,
64bit integers are represented as arrays of 2 numbers, first the high word, then the low word.
sub shl {
my ($a, $n)= @_;
return ($a<<$n) if ($n>0);
return ($a>>-$n) if ($n<0);
return $a;
}
sub shl64 {
my ($a, $n)= @_;
return [ ($a->[0]<<$n)|($a->[1]>>(32-$n)), ($a->[1]<<$n) ] if ($n>0);
return [ ($a->[0]>>-$n), ($a->[0]<<32+$n)|($a->[1]>>-$n) ] if ($n<0);
return [ $a->[0], $a->[1] ];
}
sub intadd {
my ($a, $b)= @_;
return int($a+$b);
}
sub intsub {
my ($a, $b)= @_;
return int($a-$b);
}
sub intmul64 {
my ($a, $b)= @_;
my $prod= $a*$b;
return [ int($prod/2**32), int($prod) ];
}
sub intdiv64 {
my ($a64, $b)= @_;
return int(($a64->[0]*2**32 + $a64->[1])/$b);
}
here are some generic fixed point operations:
sub fxpshl {
my ($a, $shift, $n)= @_;
return { a=>shl($a->{a}, $shift+$n-$a->{n}), n=>$n };
}
# returns msb such that 2^msb <= $nr < 2*2^msb
sub GetMsb {
my ($a)= @_;
my $val= $a->{a};
my $shifts= 0;
while($val>1) {
$val >>=1;
$shifts++;
}
return $shifts;
}
sub fxpdup {
my ($a, $n)= @_;
return {a=>shl($a->{a}, $n-$a->{n}), n=>$n};
}
sub tofxp {
my ($a, $n)= @_;
return {a=>rint($a*(1<<$n)), n=>$n};
}
sub toval {
my ($a)= @_;
return $a->{a}/(1<<$a->{n});
}
sub tostr {
my ($a)= @_;
return sprintf("%8.6f[%08lx:%2d]", toval($a), $a->{a}, $a->{n});
}
sub rint {
my ($a)= @_;
if ($a>0) {
return int($a+0.5);
}
elsif ($a<0) {
return int($a-0.5);
}
else {
return 0;
}
}
sub fxpadd {
my ($a, $b, $n) = @_;
if ($b->{n} > $a->{n}) {
return {a=>shl( intadd(shl($a->{a}, $b->{n}-$a->{n}) , $b->{a}), $n-$b->{n}), n=>$n};
}
else {
return {a=>shl( intadd($a->{a} , shl($b->{a}, $a->{n}-$b->{n})), $n-$a->{n}), n=>$n};
}
}
sub fxpsub {
my ($a, $b, $n) = @_;
if ($b->{n} > $a->{n}) {
return {a=>shl( intsub(shl($a->{a}, $b->{n}-$a->{n}) , $b->{a}), $n-$b->{n}), n=>$n};
}
else {
return {a=>shl(intsub($a->{a} , shl($b->{a}, $a->{n}-$b->{n})), $n-$a->{n}), n=>$n};
}
}
sub fxpmul {
my ($a, $b, $n) = @_;
return {a=>shl64(intmul64($a->{a} , $b->{a}), $n-$a->{n}-$b->{n})->[1], n=>$n};
}
sub fxpdiv {
my ($a, $b, $n)= @_;
return {a=>intdiv64(shl64([0,$a->{a}], $n-$a->{n}+$b->{n}), $b->{a}), n=>$n};
}
the reciprocal can be approximated quickly using newton rapson to to solve 'x-1/y=0'
f(y)= x-1/y f'(y)= 1/y^2 y-f(y)/f'(y) = y-xy^2+y = y(2-x*y) y[k+1] = y[k] - f(y[k]) / f'(y[k]) y1= y *** this initial estimate is done by table lookup. y2= y1*(2-x*y1)
sub fxpnewtdiv {
my ($a, $b, $n)= @_;
return fxpmul($a, newtreciprocal($b, $n), $n);
}
# todo: write this in terms of int operations
sub newtreciprocal {
my ($x, $n)= @_;
my $y= GetInitialEstimate($x); ## cheating here
for (my $i= 0 ; $i < 10 ; $i++)
{
$y= fxpmul($y, fxpsub(tofxp(2, $n), fxpmul($x, $y, $n), $n), $n);
}
return $y;
}
# initial estimate for reciprocal calc: use highest bit.
# ( if x=2^n + 2^(n-?) + ... -> return 2^-n
sub GetInitialEstimate {
my ($x)= @_;
my $val= toval($x);
my $l2= int(log($val)/log(2));
return tofxp(2**-$l2, $x->{n});
}
sub fxpmuldiv {
my ($a, $b, $n)= @_;
my $msbit=GetMsb($b);
my $shiftcount= $b->{n}-$msbit;
my $normb= fxpshl($b, $shiftcount, $b->{n});
my $norma= fxpshl($a, $shiftcount, $a->{n});
for (1..6) {
$b= fxpmul(fxpsub(tofxp(2,$n), $normb, $n), $b, $n);
$a= fxpmul(fxpsub(tofxp(2,$n), $norma, $n), $a, $n);
}
return $a;
}
a(i) = 1/(1-2^-i) , i=1...
r(L,nL)= ... r(A,nA)
r(A,nA)= 2^scale * prod( a(i) )
where 'scale' is such that 2<= r(A,nA)*2^-scale < 1
r(L,nL)= log(2^scale * prod( a(i) ) )
= scale*log(2) + sum( log(a(i)) )
L*2^-nL = log(A*2^-nA) = log(A)-nA*log(2)
sub fxplog {
my ($a, $n)= @_;
if ($a->{a}<=0) {
return;
}
my $base= $n;
# lg2 contains values [ 2*2^n .. 2^n >
my @va2= map { rint(2**$base/(1-2**(-$_))) } (1..$base); unshift @va2, 0;
my @lg2= map { -rint(2**$n*log(1-2**(-$_))) } (1..$base); unshift @lg2, 0;
my $val= $a->{a};
# scale $a such that it is just over 2*2^n : bit (n+1) = 1
my $msb= GetMsb($a);
$val = shl($val, 1+$base-$msb);
my $l= -rint((1+$a->{n}-$msb)*log(2)*2**$n); #-$a->{n};
for (1..$base)
{
while ($val>=$va2[$_])
{
$val -= ($val >> $_);
$l += $lg2[$_];
}
}
my $res= {a=>$l, n=>$n};
return $res;
}
sub fxpsqrt {
my ($a, $n)= @_;
# a= (a+1)/2 - initial estimate
my $sqrt= fxpshl(fxpadd($a, tofxp(1,$n), $n), -1, $n);
for (0..7) {
$sqrt= fxpshl(fxpadd($sqrt, fxpdiv($a, $sqrt, $n), $n), -1, $n);
}
return $sqrt;
}
sub fxpexp {
my ($a, $n)= @_;
my @fact= (1);
push @fact, $fact[-1]*$_ for (1..8);
my @fxfact= map { tofxp($_, $n) } @fact;
my $result= tofxp(1, $n);
my $xn= fxpdup($a, $n);
for (1..8) {
$result= fxpadd($result, fxpdiv($xn, $fxfact[$_], $n), $n);
$xn= fxpmul($xn, $a, $n);
}
return $result;
}
# pow can be implemented as follows:
#
#
# a^sum(2^b(i)) = prod ( a^(2^b(i)) )
#
# res= 1;
# aa= a;
# for (i=fixedpoint ; i<32 ; i++)
# {
# if (b&bit(i))
# {
# res *= aa;
# }
# aa *= aa;
# }
# aa= sqrt(a);
# for (i=fixedpoint-1 ; i>=0 ; i--)
# {
# if (b&bit(i))
# {
# res *= aa;
# }
# aa = sqrt(aa);
# }
#
#
# a^b can then be implemented as exp2(b*log2(a))
#
- none yet.
- none yet