📄 rr.cpp
字号:
RR::SetPrecision(p + NumBits(p) + 10);
RR t2, s, s1, t;
long i;
s = 0;
t = 1;
for (i = 1; ; i++) {
add(s1, s, t);
if (s == s1) break;
xcopy(s, s1);
mul(t, t, f);
div(t, t, i);
}
xcopy(t2, s);
RR::SetPrecision(p);
mul(res, t1, t2);
}
void ReallyComputeLn2(RR& res)
{
long p = RR::precision();
RR::SetPrecision(p + NumBits(p) + 10);
RR s, s1, t, t1;
s = 0;
t = 0.5;
t1 = 0.5;
long i;
for (i = 2; ; i++) {
add(s1, s, t);
if (s == s1) break;
xcopy(s, s1);
mul(t1, t1, 0.5);
div(t, t1, i);
}
RR::SetPrecision(p);
xcopy(res, s);
}
void ComputeLn2(RR& res)
{
static long prec = 0;
static RR ln2;
long p = RR::precision();
if (prec <= p + 10) {
prec = p + 20;
RR::SetPrecision(prec);
ReallyComputeLn2(ln2);
RR::SetPrecision(p);
}
xcopy(res, ln2);
}
long Lg2(const RR& x)
{
return NumBits(x.mantissa()) + x.exponent();
}
void log(RR& res, const RR& x)
{
if (x <= 0) Error("argument to log must be positive");
long p = RR::precision();
RR::SetPrecision(p + NumBits(p) + 10);
RR y;
long n;
// re-write x = 2^n * (1 - y), where -1/2 < y < 1/4 (so 3/4 < 1-y < 3/2)
if (x > 0.75 && x < 1.5) {
n = 0;
sub(y, 1, x);
}
else {
n = Lg2(x) - 1;
RR t;
power2(t, -n);
mul(t, t, x);
while (t > 1.5) {
mul(t, t, 0.5);
n++;
}
sub(y, 1, t);
}
// compute s = - ln(1-y) by power series expansion
RR s, s1, t, t1;
s = 0;
xcopy(t, y);
xcopy(t1, y);
long i;
for (i = 2; ; i++) {
add(s1, s, t);
if (s == s1) break;
xcopy(s, s1);
mul(t1, t1, y);
div(t, t1, i);
}
if (n == 0)
t = 0;
else {
ComputeLn2(t);
mul(t, t, n);
}
RR::SetPrecision(p);
sub(res, t, s);
}
void ComputeLn10(RR& res)
{
static long prec = 0;
static RR ln10;
long p = RR::precision();
if (prec <= p + 10) {
prec = p + 20;
RR::SetPrecision(prec);
log(ln10, to_RR(10));
RR::SetPrecision(p);
}
xcopy(res, ln10);
}
void log10(RR& res, const RR& x)
{
long p = RR::precision();
RR::SetPrecision(p + 10);
RR ln10, t1, t2;
ComputeLn10(ln10);
log(t1, x);
div(t2, t1, ln10);
RR::SetPrecision(p);
xcopy(res, t2);
}
void expm1(RR& res, const RR& x)
{
if (x < -0.5 || x > 0.5) {
RR t;
exp(t, x);
sub(res, t, 1);
return;
}
long p = RR::precision();
RR::SetPrecision(p + NumBits(p) + 10);
RR f;
xcopy(f, x);
RR s, s1, t;
long i;
s = 0;
xcopy(t, f);
for (i = 2; ; i++) {
add(s1, s, t);
if (s == s1) break;
xcopy(s, s1);
mul(t, t, f);
div(t, t, i);
}
RR::SetPrecision(p);
xcopy(res, s);
}
void log1p(RR& res, const RR& x)
{
long p = RR::precision();
RR y;
if (x < -0.5 || x > 0.5) {
RR::SetPrecision(p + 10);
log(y, 1 + x);
RR::SetPrecision(p);
xcopy(res, y);
return;
}
RR::SetPrecision(p + NumBits(p) + 10);
negate(y, x);
// compute s = - ln(1-y) by power series expansion
RR s, s1, t, t1;
s = 0;
xcopy(t, y);
xcopy(t1, y);
long i;
for (i = 2; ; i++) {
add(s1, s, t);
if (s == s1) break;
xcopy(s, s1);
mul(t1, t1, y);
div(t, t1, i);
}
RR::SetPrecision(p);
negate(res, s);
}
void pow(RR& res, const RR& x, const RR& y)
{
if (y == 0) {
res = 1;
return;
}
if (x == 0) {
res = 0;
return;
}
if (x == 1) {
res = 1;
return;
}
if (x < 0) {
Error("pow: sorry...first argument to pow must be nonnegative");
}
long p = RR::precision();
// calculate working precison...one could use p + NTL_BITS_PER_LONG + 10,
// for example, but we want the behaviour to be machine independent.
// so we calculate instead a rough approximation to log |y log(x)|
RR t1, t2;
long k;
if (x > 0.5 && x < 1.5) {
xcopy(t1, x - 1);
k = Lg2(t1);
}
else {
k = NumBits(Lg2(x));
}
k += Lg2(y);
if (k > NTL_BITS_PER_LONG+10) Error("RR: overflow");
if (k < 0) k = 0;
RR::SetPrecision(p + k + 10);
t1 = y*log(x);
RR::SetPrecision(p);
t2 = exp(t1);
res = t2;
}
void ReallyComputePi(RR& res)
{
long p = RR::precision();
RR::SetPrecision(p + NumBits(p) + 10);
RR sum1;
RR s, s1, t, t1;
s = 0;
t = 0.5;
t1 = 0.5;
long i;
for (i = 3; ; i+=2) {
add(s1, s, t);
if (s == s1) break;
xcopy(s, s1);
mul(t1, t1, -0.25);
div(t, t1, i);
}
xcopy(sum1, s);
RR g;
inv(g, to_RR(3)); // g = 1/3
s = 0;
xcopy(t, g);
xcopy(t1, g);
sqr(g, g);
negate(g, g); // g = -1/9
for (i = 3; ; i+=2) {
add(s1, s, t);
if (s == s1) break;
xcopy(s, s1);
mul(t1, t1, g);
div(t, t1, i);
}
add(s, s, sum1);
mul(s, s, 4);
RR::SetPrecision(p);
xcopy(res, s);
}
void ComputePi(RR& res)
{
static long prec = 0;
static RR pi;
long p = RR::precision();
if (prec <= p + 10) {
prec = p + 20;
RR::SetPrecision(prec);
ReallyComputePi(pi);
RR::SetPrecision(p);
}
xcopy(res, pi);
}
void sin(RR& res, const RR& x)
{
if (x == 0) {
res = 0;
return;
}
if (Lg2(x) > 1000)
Error("sin: sorry...argument too large in absolute value");
long p = RR::precision();
RR pi, t1, f;
RR n;
// we want to make x^2 < 3, so that the series for sin(x)
// converges nicely, without any nasty cancellations in the
// first terms of the series.
RR::SetPrecision(p + NumBits(p) + 10);
if (x*x < 3) {
xcopy(f, x);
}
else {
// we want to write x/pi = n + f, |f| < 1/2....
// but we have to do *this* very carefully, so that f is computed
// to precision > p. I know, this is sick!
long p1;
p1 = p + Lg2(x) + 20;
for (;;) {
RR::SetPrecision(p1);
ComputePi(pi);
xcopy(t1, x/pi);
xcopy(n, floor(t1));
xcopy(f, t1 - n);
if (f > 0.5) {
n++;
xcopy(f, t1 - n);
}
if (f == 0 || p1 < p - Lg2(f) + Lg2(n) + 10) {
// we don't have enough bits of f...increase p1 and continue
p1 = p1 + max(20, p1/10);
}
else
break;
}
RR::SetPrecision(p + NumBits(p) + 10);
ComputePi(pi);
xcopy(f, pi * f);
if (n != 0 && n.exponent() == 0) {
// n is odd, so we negate f, which negates sin(f)
xcopy(f, -f);
}
}
// Boy, that was painful, but now its over, and we can simply apply
// the series for sin(f)
RR t2, s, s1, t;
long i;
s = 0;
xcopy(t, f);
for (i = 3; ; i=i+2) {
add(s1, s, t);
if (s == s1) break;
xcopy(s, s1);
mul(t, t, f);
mul(t, t, f);
div(t, t, i-1);
div(t, t, i);
negate(t, t);
}
RR::SetPrecision(p);
xcopy(res, s);
}
void cos(RR& res, const RR& x)
{
if (x == 0) {
res = 1;
return;
}
if (Lg2(x) > 1000)
Error("cos: sorry...argument too large in absolute value");
long p = RR::precision();
RR pi, t1, f;
RR n;
// we want to write x/pi = (n+1/2) + f, |f| < 1/2....
// but we have to do *this* very carefully, so that f is computed
// to precision > p. I know, this is sick!
long p1;
p1 = p + Lg2(x) + 20;
for (;;) {
RR::SetPrecision(p1);
ComputePi(pi);
xcopy(t1, x/pi);
xcopy(n, floor(t1));
xcopy(f, t1 - (n + 0.5));
if (f == 0 || p1 < p - Lg2(f) + Lg2(n) + 10) {
// we don't have enough bits of f...increase p1 and continue
p1 = p1 + max(20, p1/10);
}
else
break;
}
RR::SetPrecision(p + NumBits(p) + 10);
ComputePi(pi);
xcopy(f, pi * f);
if (n == 0 || n.exponent() != 0) {
// n is even, so we negate f, which negates sin(f)
xcopy(f, -f);
}
// Boy, that was painful, but now its over, and we can simply apply
// the series for sin(f)
RR t2, s, s1, t;
long i;
s = 0;
xcopy(t, f);
for (i = 3; ; i=i+2) {
add(s1, s, t);
if (s == s1) break;
xcopy(s, s1);
mul(t, t, f);
mul(t, t, f);
div(t, t, i-1);
div(t, t, i);
negate(t, t);
}
RR::SetPrecision(p);
xcopy(res, s);
}
ostream& operator<<(ostream& s, const RR& a)
{
if (IsZero(a)) {
s << "0";
return s;
}
long old_p = RR::precision();
// we compute new_p and log_10_a precisely using sufficient
// precision---this is necessary to achieve accuracy and
// platform independent behaviour
long temp_p = max(NumBits(RR::OutputPrecision()),
NumBits(Lg2(a))) + 10;
RR::SetPrecision(temp_p);
RR ln2, ln10, log_2_10;
ComputeLn2(ln2);
ComputeLn10(ln10);
log_2_10 = ln10/ln2;
long new_p = to_long(RR::OutputPrecision()*log_2_10) + 20;
long log_10_a = to_long(Lg2(a)/log_2_10);
RR::SetPrecision(new_p);
RR b;
long neg;
if (a < 0) {
negate(b, a);
neg = 1;
}
else {
xcopy(b, a);
neg = 0;
}
long k = RR::OutputPrecision() - log_10_a;
RR c, d;
power(c, to_RR(10), RR::OutputPrecision());
power(d, to_RR(10), log_10_a);
div(b, b, d);
mul(b, b, c);
while (b < c) {
mul(b, b, 10);
k++;
}
while (b >= c) {
div(b, b, 10);
k--;
}
add(b, b, 0.5);
k = -k;
ZZ B;
conv(B, b);
long bp_len = RR::OutputPrecision()+10;
char *bp = NTL_NEW_OP char[bp_len];
if (!bp) Error("RR output: out of memory");
long len, i;
len = 0;
do {
if (len >= bp_len) Error("RR output: buffer overflow");
bp[len] = IntValToChar(DivRem(B, B, 10));
len++;
} while (B > 0);
for (i = 0; i < len/2; i++) {
char tmp;
tmp = bp[i];
bp[i] = bp[len-1-i];
bp[len-1-i] = tmp;
}
i = len-1;
while (bp[i] == '0') i--;
k += (len-1-i);
len = i+1;
bp[len] = '\0';
if (k > 3 || k < -len - 3) {
// use scientific notation
if (neg) s << "-";
s << "0." << bp << "e" << (k + len);
}
else if (k >= 0) {
if (neg) s << "-";
s << bp;
for (i = 0; i < k; i++)
s << "0";
}
else if (k <= -len) {
if (neg) s << "-";
s << "0.";
for (i = 0; i < -len-k; i++)
s << "0";
s << bp;
}
else {
if (neg) s << "-";
for (i = 0; i < len+k; i++)
s << bp[i];
s << ".";
for (i = len+k; i < len; i++)
s << bp[i];
}
RR::SetPrecision(old_p);
delete [] bp;
return s;
}
NTL_END_IMPL
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -