KGC_TEST/miracl/source/floating.cpp

678 lines
11 KiB
C++

/*
* MIRACL C++ Float functions float.cpp
*
* AUTHOR : M. Scott
*
* PURPOSE : Implementation of Class Float functions
*/
#include <iostream>
#include "floating.h"
using namespace std;
#define TAN 1
#define SIN 2
#define COS 3
static Float *spi;
static int precision=4; // must be power of 2
static BOOL pi_cooked=FALSE;
Float makefloat(int a,int b)
{
return (Float)a/b;
}
Float fabs(const Float& f)
{
Float r=f;
if (r.m<0) r.m.negate();
return r;
}
int norm(int type,Float& y)
{ // convert y to 1st quadrant angle, and return sign
int s=PLUS;
Float pi,w,t;
if (y.sign()<0)
{
y.negate();
if (type!=COS) s=-s;
}
pi=fpi();
w=pi/2;
if (fcomp(y,w)<=0) return s;
w=2*pi;
if (fcomp(y,w)>0)
{ // reduce mod 2.pi
t=y/w;
t=trunc(t);
t*=w;
y-=t;
}
if (fcomp(y,pi)>0)
{
y-=pi;
if (type!=TAN) s=(-s);
}
w=pi/2;
if (fcomp(y,w)>0)
{
y=pi-y;
if (type!=SIN) s=(-s);
}
return s;
}
Float cos(const Float& f)
{ // see Brent - "The complexity of multiprecision arithmetic"
int i,q,sgn;
Float t,r,y,x=f;
Float one=(Float)1;
Big lambda;
sgn=norm(COS,x);
if (x.iszero()) return sgn*one;
q=isqrt(MIRACL*precision,2);
lambda=pow((Big)2,q);
x/=(Float)lambda;
r=0; y=one; x*=x;
for (i=2;i<=q+2;i+=2)
{
t=x; t.negate(); t/=(i*(i-1)); y*=t;
// y*=(-x/(i*(i-1)));
if (!r.add(y)) break;
}
for (i=0;i<q;i++)
{
r+=one;
r*=r;
r-=one;
r*=2;
}
r+=one;
if (sgn==MINUS) r.negate();
return r;
}
Float sin(const Float& f)
{ // see Brent
int i,q,sgn;
Float t,r,y,x=f;
Big lambda;
sgn=norm(SIN,x);
if (x.iszero()) return (Float)0;
q=isqrt(MIRACL*precision,2);
lambda=pow((Big)2,q);
x/=(Float)lambda;
r=x; y=x; x*=x;
for (i=3;i<=q+2;i+=2)
{
t=x; t.negate(); t/=(i*(i-1)); y*=t;
// y*=(-x/(i*(i-1)));
if (!r.add(y)) break;
}
for (i=0;i<q;i++) r=2*r*sqrt(1-r*r);
if (sgn==MINUS) r.negate();
return r;
}
int fcomp(const Float& a,const Float& b)
{
Big x,y;
int sa,sb;
sa=a.sign();
sb=b.sign();
if (sa>sb) return 1;
if (sa<sb) return -1;
if (sa==0 && sb==0) return 0;
if (a.e>b.e) return sa;
if (a.e<b.e) return -sa;
x=a.m; y=b.m;
x.shift(precision-length(x)); // make them precision length
y.shift(precision-length(y));
if (x>y) return 1;
if (x<y) return -1;
return 0;
}
Big Float::trunc(Float *rem)
{
Big b=0;
if (iszero()) return b;
b=shift(m,e-length(m));
if (rem!=NULL) *rem=*this-(Float)b;
return b;
}
Big trunc(const Float& f)
{
Big b=0;
if (f.iszero()) return b;
b=shift(f.m,f.e-length(f.m));
return b;
}
Float exp(const Float &a)
{ // from Brent
int i;
Float t,x,y,r,one=(Float)1;
int q;
Big lambda;
if (a.iszero()) return one;
x=a;
q=isqrt(MIRACL*precision,2);
lambda=pow((Big)2,q);
x/=(Float)lambda;
r=0; y=one;
for (i=1;i<=q+2;i++)
{
t=x; t/=i; y*=t;
// y*=(x/i);
if (!r.add(y)) break;
}
for (i=0;i<q;i++)
{
r+=one;
r*=r;
if (i<q-1) r-=one;
}
return r;
}
Float fpi(void)
{ // Gauss-Legendre iteration
Float r;
Float x,y,w,a,b,t=1;
int p;
if (pi_cooked) return *spi;
t/=4;
a=1;
b=2;
b=sqrt(b);
b/=2;
for (p=1;p<(MIRACL*precision)/4;p*=2)
{
x=(a+b)/2;
y=sqrt(a*b);
w=a-x;
t-=(p*(w*w));
a=x;
b=y;
}
r=a+b;
r=(r*r)/(4*t);
pi_cooked=TRUE;
spi=new Float();
*spi=r;
return r;
}
Float reciprocal(const Float &f)
{
Float x,v=f;
double d;
int ke,prec,keep=precision;
if (v.iszero())
{
cout << "Divide by zero error from float" << endl;
exit(0);
}
// scale v first !
ke=v.e; v.e=0;
d=todouble(v); d=1.0/d; x=d; // initial approximation
for (prec=1;prec<=keep;prec*=2)
{ // Newtons method
if (prec<=4) precision=4;
else precision=prec;
x=2*x-(x*x)*v;
}
x=2*x-(x*x)*v; // .. and one for luck
// .. and rescale answer
x.e-=ke;
return x;
}
Float nroot(const Float &f,int n)
{
Float x,v=f;
double d;
BOOL minus;
int ke,prec,keep=precision;
if (v.iszero()) return (Float)0;
if (n%2==0 && f.m<0)
{
cout << "Even root of a negative number error from Float" << endl;
exit(0);
}
if (n==(-1))
{
x=reciprocal(v);
return x;
}
if (n==1) return v;
minus=FALSE;
if (n<0)
{
minus=TRUE;
n=(-n);
}
// rescale..
ke=v.e/n; v.e%=n;
d=todouble(v); d=pow(d,1.0/(double)n); x=d;
for (prec=1;prec<=keep;prec*=2)
{ // Newtons method
if (prec<=4) precision=4;
else precision=prec;
x=(v/pow(x,n-1)+(n-1)*x)/n;
}
x=(v/pow(x,n-1)+(n-1)*x)/n; // .. and one for luck
x.e+=ke;
if (minus) x=reciprocal(x);
return x;
}
Float sqrt(const Float &f)
{
Float x,v=f;
double d;
int ke,prec,keep=precision;
if (v.iszero()) return (Float)0;
if (f.m<0)
{
cout << "Square root of a negative number error from Float" << endl;
exit(0);
}
// rescale..
ke=v.e/2; v.e%=2;
d=todouble(v); d=sqrt(d); x=d;
for (prec=1;prec<=keep;prec*=2)
{ // Newtons method
if (prec<=4) precision=4;
else precision=prec;
x=(v/x+x)/2;
}
x=(v/x+x)/2; // one for luck....
x.e+=ke;
return x;
}
Float pow(const Float &f,int n)
{
Float x,r=1;
if (n==0) return r;
if (f.iszero()) return f;
x=f;
if (n<0)
{
n=(-n);
x=reciprocal(x);
}
if (n==1) return x;
forever
{
if (n%2!=0) r*=x;
n/=2;
if (n==0) break;
x*=x;
}
return r;
}
void Float::negate() const
{
if (m.iszero()) return;
m.negate();
}
BOOL Float::iszero() const
{
if (m.iszero()) return TRUE;
else return FALSE;
}
BOOL Float::isone() const
{
if (m.isone() && e==1) return TRUE;
else return FALSE;
}
int Float::sign() const
{
if (m>0) return PLUS;
if (m<0) return MINUS;
return 0;
}
Float operator-(const Float& f)
{
Float r=f;
r.m.negate();
return r;
}
Float& Float::operator=(const Float &f)
{
e=f.e;
m=f.m;
return *this;
}
Float::Float(double d)
{
Big t;
double ip,word;
int c,s=PLUS;
if (d<0) {s=MINUS; d=-d;}
m=0;
e=0;
if (d==0.0) return;
c=1;
word=pow(2.0,(double)MIRACL);
while (d>=word)
{
d/=word; c++;
}
while (d<1.0)
{
d*=word; c--;
}
d=modf(d,&ip);
m=(mr_small)ip;
forever
{
d*=word;
d=modf(d,&ip);
t=(mr_small)ip;
m=shift(m,1)+t;
if (d==0.0 || length(m)>precision) break;
}
e=c;
if (s==MINUS) m.negate();
}
Float& Float::operator=(double d)
{
Float t=d;
*this=t;
return *this;
}
double todouble(const Float &f)
{
int i,s;
Big x=f.m;
double word,d=0.0;
mr_small dig;
if (f.iszero()) return d;
if (f.m>=0) s=PLUS;
else {s=MINUS; x.negate();}
word=pow(2.0,(double)MIRACL);
for (i=0;i<length(x);i++)
{
dig=x[i];
d+=(double)dig;
d/=word;
}
if (f.e>0) for (i=0;i<f.e;i++) d*=word;
if (f.e<0) for (i=0;i<-f.e;i++) d/=word;
if (s==MINUS) d=-d;
return d;
}
BOOL Float::add(const Float &b)
{ // return TRUE if this is affected, FALSE if b has no effect
if (b.iszero()) return FALSE;
if (iszero()) {*this=b; return TRUE;}
Big y=b.m;
m.shift(precision-length(m)); // make them precision length
y.shift(precision-length(y));
if (e>=b.e)
{
if (e-b.e>precision) return FALSE;
y.shift(b.e-e);
m+=y;
if (m.iszero()) e=0;
else e+=length(m)-precision;
}
else
{
if (b.e-e>precision) {*this=b; return TRUE;}
m.shift(e-b.e);
m+=y;
if (m.iszero()) e=0;
else e=b.e+length(m)-precision;
}
m.shift(precision-length(m));
return TRUE;
}
Float& Float::operator+=(const Float &b)
{
add(b);
return *this;
}
BOOL Float::sub(const Float &b)
{
if (b.iszero()) return FALSE;
if (iszero()) {*this=-b; return TRUE;}
Big y=b.m;
m.shift(precision-length(m)); // make them precision length
y.shift(precision-length(y));
if (e>=b.e)
{
if (e-b.e>precision) return FALSE;
y.shift(b.e-e);
m-=y;
if (m.iszero()) e=0;
else e+=length(m)-precision;
}
else
{
if (b.e-e>precision) {*this=-b; return TRUE;}
m.shift(e-b.e);
m-=y;
if (m.iszero()) e=0;
else e=b.e+length(m)-precision;
}
m.shift(precision-length(m));
return TRUE;
}
Float& Float::operator-=(const Float &b)
{
sub(b);
return *this;
}
Float operator+(const Float& a,const Float &b)
{
Float r=a;
r+=b;
return r;
}
Float operator-(const Float& a,const Float &b)
{
Float r=a;
r-=b;
return r;
}
Float& Float::operator*=(const Float& b)
{
BOOL extra;
if (iszero() || b.isone()) return *this;
if (b.iszero() || isone()) {*this=b; return *this;}
if (&b==this)
{
if (m<0) m.negate(); // result will be positive
m.shift(precision-length(m)); // make them precision length
extra=fmth(precision,m,m,m);
e+=e;
if (extra) e--;
}
else
{
Big y=b.m;
int s=PLUS;
if (m<0) { s*=MINUS; m.negate(); }
if (y<0) { s*=MINUS; y.negate(); }
m.shift(precision-length(m)); // make them precision length
y.shift(precision-length(y));
extra=fmth(precision,m,y,m);
if (s<0) m.negate();
e+=b.e;
if (extra) e--;
}
return *this;
}
Float operator*(const Float& a,const Float& b)
{
Float r=a;
r*=b;
return r;
}
Float operator*(const Float& a,int b)
{
Float r=a;
r*=b;
return r;
}
Float operator*(int a,const Float& b)
{
Float r=b;
r*=a;
return r;
}
Float& Float::operator*=(int x)
{
int olm=length(m);
m*=x;
e+=length(m)-olm;
m.shift(precision-length(m));
return *this;
}
Float& Float::operator/=(int x)
{
int olm;
if (x==1) return *this;
m.shift(precision-length(m));
olm=length(m);
m/=x;
e+=length(m)-olm;
m.shift(precision-length(m));
return *this;
}
Float operator/(const Float& f,int x)
{
Float r=f;
r/=x;
return r;
}
Float& Float::operator/=(const Float &f)
{
Float g=reciprocal(f);
*this*=g;
return *this;
}
Float operator/(const Float& a,const Float &b)
{
Float r=reciprocal(b);
r*=a;
return r;
}
void setprecision(int p) {precision=(1<<p);}
ostream& operator<<(ostream& s,const Float &f)
{
return otfloat(s,f.m,f.e);
}