Browse Source

core: remove most dependencies on real number size in the various math

functions.
legacy
Sam Hocevar sam 13 years ago
parent
commit
66a2ee6a40
1 changed files with 71 additions and 57 deletions
  1. +71
    -57
      src/real.cpp

+ 71
- 57
src/real.cpp View File

@@ -487,13 +487,10 @@ real re(real const &x)
exponent = -exponent + (v.x >> 23) - (u.x >> 23); exponent = -exponent + (v.x >> 23) - (u.x >> 23);
ret.m_signexp |= (exponent - 1) & 0x7fffffffu; ret.m_signexp |= (exponent - 1) & 0x7fffffffu;


/* Five steps of Newton-Raphson seems enough for 32-bigit reals. */
real two = 2;
ret = ret * (two - ret * x);
ret = ret * (two - ret * x);
ret = ret * (two - ret * x);
ret = ret * (two - ret * x);
ret = ret * (two - ret * x);
/* FIXME: log2(BIGITS) steps of Newton-Raphson seems to be enough for
* convergence, but this hasn't been checked seriously. */
for (int i = 1; i < real::BIGITS; i *= 2)
ret = ret * (real::R_2 - ret * x);


return ret; return ret;
} }
@@ -532,22 +529,17 @@ real sqrt(real const &x)
ret.m_signexp = sign; ret.m_signexp = sign;


uint32_t exponent = (x.m_signexp & 0x7fffffffu); uint32_t exponent = (x.m_signexp & 0x7fffffffu);
exponent = ((1 << 30) + (1 << 29) -1) - (exponent + 1) / 2;
exponent = ((1 << 30) + (1 << 29) - 1) - (exponent + 1) / 2;
exponent = exponent + (v.x >> 23) - (u.x >> 23); exponent = exponent + (v.x >> 23) - (u.x >> 23);
ret.m_signexp |= exponent & 0x7fffffffu; ret.m_signexp |= exponent & 0x7fffffffu;


/* Five steps of Newton-Raphson seems enough for 32-bigit reals. */
real three = 3;
ret = ret * (three - ret * ret * x);
ret.m_signexp--;
ret = ret * (three - ret * ret * x);
ret.m_signexp--;
ret = ret * (three - ret * ret * x);
ret.m_signexp--;
ret = ret * (three - ret * ret * x);
ret.m_signexp--;
ret = ret * (three - ret * ret * x);
ret.m_signexp--;
/* FIXME: log2(BIGITS) steps of Newton-Raphson seems to be enough for
* convergence, but this hasn't been checked seriously. */
for (int i = 1; i < real::BIGITS; i *= 2)
{
ret = ret * (real::R_3 - ret * ret * x);
ret.m_signexp--;
}


return ret * x; return ret * x;
} }
@@ -559,7 +551,7 @@ real fabs(real const &x)
return ret; return ret;
} }


static real fastlog(real const &x)
static real fast_log(real const &x)
{ {
/* This fast log method is tuned to work on the [1..2] range and /* This fast log method is tuned to work on the [1..2] range and
* no effort whatsoever was made to improve convergence outside this * no effort whatsoever was made to improve convergence outside this
@@ -577,24 +569,25 @@ static real fastlog(real const &x)
* would also impact the final precision. For now we stick with one * would also impact the final precision. For now we stick with one
* sqrt() call. */ * sqrt() call. */
real y = sqrt(x); real y = sqrt(x);
real z = (y - (real)1) / (y + (real)1), z2 = z * z, zn = z2;
real sum = 1.0;
real z = (y - real::R_1) / (y + real::R_1), z2 = z * z, zn = z2;
real sum = real::R_1;


for (int i = 3; i < 200; i += 2)
for (int i = 3; ; i += 2)
{ {
sum += zn / (real)i;
real newsum = sum + zn / (real)i;
if (newsum == sum)
break;
sum = newsum;
zn *= z2; zn *= z2;
} }


return z * (sum << 2); return z * (sum << 2);
} }


static real LOG_2 = fastlog((real)2);

real log(real const &x) real log(real const &x)
{ {
/* Strategy for log(x): if x = 2^E*M then log(x) = E log(2) + log(M), /* Strategy for log(x): if x = 2^E*M then log(x) = E log(2) + log(M),
* with the property that M is in [1..2[, so fastlog() applies here. */
* with the property that M is in [1..2[, so fast_log() applies here. */
real tmp = x; real tmp = x;
if (x.m_signexp >> 31 || x.m_signexp == 0) if (x.m_signexp >> 31 || x.m_signexp == 0)
{ {
@@ -603,7 +596,7 @@ real log(real const &x)
return tmp; return tmp;
} }
tmp.m_signexp = (1 << 30) - 1; tmp.m_signexp = (1 << 30) - 1;
return (real)(x.m_signexp - (1 << 30) + 1) * LOG_2 + fastlog(tmp);
return (real)(x.m_signexp - (1 << 30) + 1) * real::R_LN2 + fast_log(tmp);
} }


real exp(real const &x) real exp(real const &x)
@@ -624,14 +617,17 @@ real exp(real const &x)
* real x1 = exp(x0) * real x1 = exp(x0)
* return x1 * 2^E0 * return x1 * 2^E0
*/ */
int e0 = x / LOG_2;
real x0 = x - (real)e0 * LOG_2;
real x1 = 1.0, fact = 1.0, xn = x0;
int e0 = x / real::R_LN2;
real x0 = x - (real)e0 * real::R_LN2;
real x1 = real::R_1, fact = real::R_1, xn = x0;


for (int i = 1; i < 100; i++)
for (int i = 1; ; i++)
{ {
fact *= (real)i; fact *= (real)i;
x1 += xn / fact;
real newx1 = x1 + xn / fact;
if (newx1 == x1)
break;
x1 = newx1;
xn *= x0; xn *= x0;
} }


@@ -719,11 +715,11 @@ real sin(real const &x)
if (absx > real::R_PI_2) if (absx > real::R_PI_2)
absx = real::R_PI - absx; absx = real::R_PI - absx;


real ret = 0.0, fact = 1.0, xn = absx, x2 = absx * absx;
real ret = real::R_0, fact = real::R_1, xn = absx, x2 = absx * absx;
for (int i = 1; ; i += 2) for (int i = 1; ; i += 2)
{ {
real newret = ret + xn / fact; real newret = ret + xn / fact;
if (ret == newret)
if (newret == ret)
break; break;
ret = newret; ret = newret;
xn *= x2; xn *= x2;
@@ -755,10 +751,14 @@ static real asinacos(real const &x, bool is_asin, bool is_negative)
absx = sqrt((real::R_1 - absx) >> 1); absx = sqrt((real::R_1 - absx) >> 1);


real ret = absx, xn = absx, x2 = absx * absx, fact1 = 2, fact2 = 1; real ret = absx, xn = absx, x2 = absx * absx, fact1 = 2, fact2 = 1;
for (int i = 1; i < 280; i++)
for (int i = 1; ; i++)
{ {
xn *= x2; xn *= x2;
ret += (fact1 * xn / ((real)(2 * i + 1) * fact2)) >> (i * 2);
real mul = (real)(2 * i + 1);
real newret = ret + ((fact1 * xn / (mul * fact2)) >> (i * 2));
if (newret == ret)
break;
ret = newret;
fact1 *= (real)((2 * i + 1) * (2 * i + 2)); fact1 *= (real)((2 * i + 1) * (2 * i + 2));
fact2 *= (real)((i + 1) * (i + 1)); fact2 *= (real)((i + 1) * (i + 1));
} }
@@ -819,10 +819,13 @@ real atan(real const &x)
if (absx < (real::R_1 >> 1)) if (absx < (real::R_1 >> 1))
{ {
real ret = x, xn = x, mx2 = -x * x; real ret = x, xn = x, mx2 = -x * x;
for (int i = 3; i < 100; i += 2)
for (int i = 3; ; i += 2)
{ {
xn *= mx2; xn *= mx2;
ret += xn / (real)i;
real newret = ret + xn / (real)i;
if (newret == ret)
break;
ret = newret;
} }
return ret; return ret;
} }
@@ -833,13 +836,16 @@ real atan(real const &x)
{ {
real y = real::R_1 - absx; real y = real::R_1 - absx;
real yn = y, my2 = -y * y; real yn = y, my2 = -y * y;
for (int i = 0; i < 200; i += 2)
for (int i = 0; ; i += 2)
{ {
ret += (yn / (real)(2 * i + 1)) >> (i + 1);
real newret = ret + ((yn / (real)(2 * i + 1)) >> (i + 1));
yn *= y; yn *= y;
ret += (yn / (real)(2 * i + 2)) >> (i + 1);
newret += (yn / (real)(2 * i + 2)) >> (i + 1);
yn *= y; yn *= y;
ret += (yn / (real)(2 * i + 3)) >> (i + 2);
newret += (yn / (real)(2 * i + 3)) >> (i + 2);
if (newret == ret)
break;
ret = newret;
yn *= my2; yn *= my2;
} }
ret = real::R_PI_4 - ret; ret = real::R_PI_4 - ret;
@@ -848,17 +854,20 @@ real atan(real const &x)
{ {
real y = (absx - real::R_SQRT3) >> 1; real y = (absx - real::R_SQRT3) >> 1;
real yn = y, my2 = -y * y; real yn = y, my2 = -y * y;
for (int i = 1; i < 200; i += 6)
for (int i = 1; ; i += 6)
{ {
ret += (yn / (real)i) >> 1;
real newret = ret + ((yn / (real)i) >> 1);
yn *= y; yn *= y;
ret -= (real::R_SQRT3 >> 1) * yn / (real)(i + 1);
newret -= (real::R_SQRT3 >> 1) * yn / (real)(i + 1);
yn *= y; yn *= y;
ret += yn / (real)(i + 2);
newret += yn / (real)(i + 2);
yn *= y; yn *= y;
ret -= (real::R_SQRT3 >> 1) * yn / (real)(i + 3);
newret -= (real::R_SQRT3 >> 1) * yn / (real)(i + 3);
yn *= y; yn *= y;
ret += (yn / (real)(i + 4)) >> 1;
newret += (yn / (real)(i + 4)) >> 1;
if (newret == ret)
break;
ret = newret;
yn *= my2; yn *= my2;
} }
ret = real::R_PI_3 + ret; ret = real::R_PI_3 + ret;
@@ -868,10 +877,13 @@ real atan(real const &x)
real y = re(absx); real y = re(absx);
real yn = y, my2 = -y * y; real yn = y, my2 = -y * y;
ret = y; ret = y;
for (int i = 3; i < 120; i += 2)
for (int i = 3; ; i += 2)
{ {
yn *= my2; yn *= my2;
ret += yn / (real)i;
real newret = ret + yn / (real)i;
if (newret == ret)
break;
ret = newret;
} }
ret = real::R_PI_2 - ret; ret = real::R_PI_2 - ret;
} }
@@ -944,10 +956,12 @@ static real fast_pi()
real ret = 0.0, x0 = 5.0, x1 = 239.0; real ret = 0.0, x0 = 5.0, x1 = 239.0;
real const m0 = -x0 * x0, m1 = -x1 * x1, r16 = 16.0, r4 = 4.0; real const m0 = -x0 * x0, m1 = -x1 * x1, r16 = 16.0, r4 = 4.0;


/* Degree 240 is required for 512-bit mantissa precision */
for (int i = 1; i < 240; i += 2)
for (int i = 1; ; i += 2)
{ {
ret += r16 / (x0 * (real)i) - r4 / (x1 * (real)i);
real newret = ret + r16 / (x0 * (real)i) - r4 / (x1 * (real)i);
if (newret == ret)
break;
ret = newret;
x0 *= m0; x0 *= m0;
x1 *= m1; x1 *= m1;
} }
@@ -961,11 +975,11 @@ real const real::R_2 = (real)2.0;
real const real::R_3 = (real)3.0; real const real::R_3 = (real)3.0;
real const real::R_10 = (real)10.0; real const real::R_10 = (real)10.0;


real const real::R_E = exp(R_1);
real const real::R_LN2 = log(R_2);
real const real::R_LN2 = fast_log(R_2);
real const real::R_LN10 = log(R_10); real const real::R_LN10 = log(R_10);
real const real::R_LOG2E = re(R_LN2); real const real::R_LOG2E = re(R_LN2);
real const real::R_LOG10E = re(R_LN10); real const real::R_LOG10E = re(R_LN10);
real const real::R_E = exp(R_1);
real const real::R_PI = fast_pi(); real const real::R_PI = fast_pi();
real const real::R_PI_2 = R_PI >> 1; real const real::R_PI_2 = R_PI >> 1;
real const real::R_PI_3 = R_PI / R_3; real const real::R_PI_3 = R_PI / R_3;


Loading…
Cancel
Save