瀏覽代碼

real: get rid of <<= and >>= operators; we can use ldexp() instead. As a

bonus, multiplication or division by a power of two will be optimised as
a shift of the exponent.
legacy
Sam Hocevar sam 13 年之前
父節點
當前提交
366644d43c
共有 4 個檔案被更改,包括 83 行新增82 行删除
  1. +38
    -5
      src/lol/math/real.h
  2. +6
    -6
      src/lol/math/remez.h
  3. +31
    -57
      src/real.cpp
  4. +8
    -14
      test/unit/real.cpp

+ 38
- 5
src/lol/math/real.h 查看文件

@@ -55,11 +55,6 @@ public:
real const &operator *=(real const &x); real const &operator *=(real const &x);
real const &operator /=(real const &x); real const &operator /=(real const &x);


real operator <<(int x) const;
real operator >>(int x) const;
real const &operator <<=(int x);
real const &operator >>=(int x);

bool operator ==(real const &x) const; bool operator ==(real const &x) const;
bool operator !=(real const &x) const; bool operator !=(real const &x) const;
bool operator <(real const &x) const; bool operator <(real const &x) const;
@@ -112,6 +107,44 @@ public:
void hexprint() const; void hexprint() const;
void print(int ndigits = 150) const; void print(int ndigits = 150) const;


/* Additional operators using base C++ types */
#define __LOL_REAL_OP_HELPER_GENERIC(op, type) \
inline real operator op(type x) const { return *this op (real)x; } \
inline real const &operator op##=(type x) { return *this = (*this op x); }
#define __LOL_REAL_OP_HELPER_FASTMULDIV(op, type) \
inline real operator op(type x) const \
{ \
real tmp = *this; return tmp op##= x; \
} \
inline real const &operator op##=(type x) \
{ \
/* If multiplying or dividing by a power of two, take a shortcut */ \
if ((m_signexp << 1) && x && !(x & (x - 1))) \
{ \
while (x >>= 1) \
m_signexp += 1 op 2 - 1; /* 1 if op is *, -1 if op is / */ \
} \
else \
*this = *this op (real)x; \
return *this; \
}
#define __LOL_REAL_OP_HELPER_INT(type) \
__LOL_REAL_OP_HELPER_GENERIC(+, type) \
__LOL_REAL_OP_HELPER_GENERIC(-, type) \
__LOL_REAL_OP_HELPER_FASTMULDIV(*, type) \
__LOL_REAL_OP_HELPER_FASTMULDIV(/, type)
#define __LOL_REAL_OP_HELPER_FLOAT(type) \
__LOL_REAL_OP_HELPER_GENERIC(+, type) \
__LOL_REAL_OP_HELPER_GENERIC(-, type) \
__LOL_REAL_OP_HELPER_GENERIC(*, type) \
__LOL_REAL_OP_HELPER_GENERIC(/, type)

__LOL_REAL_OP_HELPER_INT(int)
__LOL_REAL_OP_HELPER_INT(unsigned int)
__LOL_REAL_OP_HELPER_FLOAT(float)
__LOL_REAL_OP_HELPER_FLOAT(double)

/* Constants */
static real const R_0; static real const R_0;
static real const R_1; static real const R_1;
static real const R_2; static real const R_2;


+ 6
- 6
src/lol/math/remez.h 查看文件

@@ -36,8 +36,8 @@ public:
{ {
m_func = func; m_func = func;
m_weight = weight; m_weight = weight;
m_k1 = (b + a) >> 1; m_k1 = (b + a) / 2;
m_k2 = (b - a) >> 1; m_k2 = (b - a) / 2;
m_invk2 = re(m_k2); m_invk2 = re(m_k2);
m_invk1 = -m_k1 * m_invk2; m_invk1 = -m_k1 * m_invk2;


@@ -139,11 +139,11 @@ public:
right.value = control[i + 1]; right.value = control[i + 1];
right.error = ChebyEval(right.value) - Value(right.value); right.error = ChebyEval(right.value) - Value(right.value);


static T limit = (T)1 >> 500; static T limit = ldexp((T)1, -500);
static T zero = (T)0; static T zero = (T)0;
while (fabs(left.value - right.value) > limit) while (fabs(left.value - right.value) > limit)
{ {
mid.value = (left.value + right.value) >> 1; mid.value = (left.value + right.value) / 2;
mid.error = ChebyEval(mid.value) - Value(mid.value); mid.error = ChebyEval(mid.value) - Value(mid.value);


if ((left.error < zero && mid.error < zero) if ((left.error < zero && mid.error < zero)
@@ -176,7 +176,7 @@ public:


for (;;) for (;;)
{ {
T c = a, delta = (b - a) >> 3; T c = a, delta = (b - a) / 8;
T maxerror = 0; T maxerror = 0;
T maxweight = 0; T maxweight = 0;
int best = -1; int best = -1;
@@ -201,7 +201,7 @@ public:
T e = maxerror / maxweight; T e = maxerror / maxweight;
if (e > final) if (e > final)
final = e; final = e;
control[i] = (a + b) >> 1; control[i] = (a + b) / 2;
break; break;
} }
} }


+ 31
- 57
src/real.cpp 查看文件

@@ -463,32 +463,6 @@ real const &real::operator /=(real const &x)
return *this = tmp / x; return *this = tmp / x;
} }


real real::operator <<(int x) const
{
real tmp = *this;
return tmp <<= x;
}

real real::operator >>(int x) const
{
real tmp = *this;
return tmp >>= x;
}

real const &real::operator <<=(int x)
{
if (m_signexp << 1)
m_signexp += x;
return *this;
}

real const &real::operator >>=(int x)
{
if (m_signexp << 1)
m_signexp -= x;
return *this;
}

bool real::operator ==(real const &x) const bool real::operator ==(real const &x) const
{ {
if ((m_signexp << 1) == 0 && (x.m_signexp << 1) == 0) if ((m_signexp << 1) == 0 && (x.m_signexp << 1) == 0)
@@ -679,7 +653,7 @@ real cbrt(real const &x)
for (int i = 1; i <= real::BIGITS; i *= 2) for (int i = 1; i <= real::BIGITS; i *= 2)
{ {
static real third = re(real::R_3); static real third = re(real::R_3);
ret = third * (x / (ret * ret) + (ret << 1)); ret = third * (x / (ret * ret) + (ret / 2));
} }


return ret; return ret;
@@ -696,7 +670,7 @@ real pow(real const &x, real const &y)
else /* x < 0 */ else /* x < 0 */
{ {
/* Odd integer exponent */ /* Odd integer exponent */
if (y == (round(y >> 1) << 1)) if (y == (round(y / 2) * 2))
return exp(y * log(-x)); return exp(y * log(-x));


/* Even integer exponent */ /* Even integer exponent */
@@ -717,7 +691,7 @@ real gamma(real const &x)
* and do the addition in this order. */ * and do the addition in this order. */
int a = ceilf(logf(2) / logf(2 * M_PI) * real::BIGITS * real::BIGIT_BITS); int a = ceilf(logf(2) / logf(2 * M_PI) * real::BIGITS * real::BIGIT_BITS);


real ret = sqrt(real::R_PI << 1); real ret = sqrt(real::R_PI * 2);
real fact_k_1 = real::R_1; real fact_k_1 = real::R_1;


for (int k = 1; k < a; k++) for (int k = 1; k < a; k++)
@@ -729,7 +703,7 @@ real gamma(real const &x)
fact_k_1 *= (real)-k; fact_k_1 *= (real)-k;
} }


ret *= pow(x + (real)(a - 1), x - (real::R_1 >> 1)); ret *= pow(x + (real)(a - 1), x - (real::R_1 / 2));
ret *= exp(-x - (real)(a - 1)); ret *= exp(-x - (real)(a - 1));


return ret; return ret;
@@ -772,7 +746,7 @@ static real fast_log(real const &x)
zn *= z2; zn *= z2;
} }


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


real log(real const &x) real log(real const &x)
@@ -875,16 +849,16 @@ real sinh(real const &x)
/* We cannot always use (exp(x)-exp(-x))/2 because we'll lose /* We cannot always use (exp(x)-exp(-x))/2 because we'll lose
* accuracy near zero. We only use this identity for |x|>0.5. If * accuracy near zero. We only use this identity for |x|>0.5. If
* |x|<=0.5, we compute exp(x)-1 and exp(-x)-1 instead. */ * |x|<=0.5, we compute exp(x)-1 and exp(-x)-1 instead. */
bool near_zero = (fabs(x) < real::R_1 >> 1); bool near_zero = (fabs(x) < real::R_1 / 2);
real x1 = near_zero ? fast_exp_sub(x, real::R_1) : exp(x); real x1 = near_zero ? fast_exp_sub(x, real::R_1) : exp(x);
real x2 = near_zero ? fast_exp_sub(-x, real::R_1) : exp(-x); real x2 = near_zero ? fast_exp_sub(-x, real::R_1) : exp(-x);
return (x1 - x2) >> 1; return (x1 - x2) / 2;
} }


real tanh(real const &x) real tanh(real const &x)
{ {
/* See sinh() for the strategy here */ /* See sinh() for the strategy here */
bool near_zero = (fabs(x) < real::R_1 >> 1); bool near_zero = (fabs(x) < real::R_1 / 2);
real x1 = near_zero ? fast_exp_sub(x, real::R_1) : exp(x); real x1 = near_zero ? fast_exp_sub(x, real::R_1) : exp(x);
real x2 = near_zero ? fast_exp_sub(-x, real::R_1) : exp(-x); real x2 = near_zero ? fast_exp_sub(-x, real::R_1) : exp(-x);
real x3 = near_zero ? x1 + x2 + real::R_2 : x1 + x2; real x3 = near_zero ? x1 + x2 + real::R_2 : x1 + x2;
@@ -895,7 +869,7 @@ real cosh(real const &x)
{ {
/* No need to worry about accuracy here; maybe the last bit is slightly /* No need to worry about accuracy here; maybe the last bit is slightly
* off, but that's about it. */ * off, but that's about it. */
return (exp(x) + exp(-x)) >> 1; return (exp(x) + exp(-x)) / 2;
} }


real frexp(real const &x, int *exp) real frexp(real const &x, int *exp)
@@ -989,7 +963,7 @@ real round(real const &x)
if (x < real::R_0) if (x < real::R_0)
return -round(-x); return -round(-x);


return floor(x + (real::R_1 >> 1)); return floor(x + (real::R_1 / 2));
} }


real fmod(real const &x, real const &y) real fmod(real const &x, real const &y)
@@ -1008,7 +982,7 @@ real sin(real const &x)
{ {
bool switch_sign = x.m_signexp & 0x80000000u; bool switch_sign = x.m_signexp & 0x80000000u;


real absx = fmod(fabs(x), real::R_PI << 1); real absx = fmod(fabs(x), real::R_PI * 2);
if (absx > real::R_PI) if (absx > real::R_PI)
{ {
absx -= real::R_PI; absx -= real::R_PI;
@@ -1075,17 +1049,17 @@ static real asinacos(real const &x, bool is_asin, bool is_negative)
* Strategy for acos(): use acos(x) = π/2 - asin(x) and try not to * Strategy for acos(): use acos(x) = π/2 - asin(x) and try not to
* lose the precision around x=1. */ * lose the precision around x=1. */
real absx = fabs(x); real absx = fabs(x);
bool around_zero = (absx < (real::R_1 >> 1)); bool around_zero = (absx < (real::R_1 / 2));


if (!around_zero) if (!around_zero)
absx = sqrt((real::R_1 - absx) >> 1); absx = sqrt((real::R_1 - absx) / 2);


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++) for (int i = 1; ; i++)
{ {
xn *= x2; xn *= x2;
real mul = (real)(2 * i + 1); real mul = (real)(2 * i + 1);
real newret = ret + ((fact1 * xn / (mul * fact2)) >> (i * 2)); real newret = ret + ldexp(fact1 * xn / (mul * fact2), -2 * i);
if (newret == ret) if (newret == ret)
break; break;
ret = newret; ret = newret;
@@ -1102,9 +1076,9 @@ static real asinacos(real const &x, bool is_asin, bool is_negative)
{ {
real adjust = is_negative ? real::R_PI : real::R_0; real adjust = is_negative ? real::R_PI : real::R_0;
if (is_asin) if (is_asin)
ret = real::R_PI_2 - adjust - (ret << 1); ret = real::R_PI_2 - adjust - ret * 2;
else else
ret = adjust + (ret << 1); ret = adjust + ret * 2;
} }


return ret; return ret;
@@ -1146,7 +1120,7 @@ real atan(real const &x)
*/ */
real absx = fabs(x); real absx = fabs(x);


if (absx < (real::R_1 >> 1)) if (absx < (real::R_1 / 2))
{ {
real ret = x, xn = x, mx2 = -x * x; real ret = x, xn = x, mx2 = -x * x;
for (int i = 3; ; i += 2) for (int i = 3; ; i += 2)
@@ -1162,17 +1136,17 @@ real atan(real const &x)


real ret = 0; real ret = 0;


if (absx < (real::R_3 >> 1)) if (absx < (real::R_3 / 2))
{ {
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 += 2) for (int i = 0; ; i += 2)
{ {
real newret = ret + ((yn / (real)(2 * i + 1)) >> (i + 1)); real newret = ret + ldexp(yn / (real)(2 * i + 1), -i - 1);
yn *= y; yn *= y;
newret += (yn / (real)(2 * i + 2)) >> (i + 1); newret += ldexp(yn / (real)(2 * i + 2), -i - 1);
yn *= y; yn *= y;
newret += (yn / (real)(2 * i + 3)) >> (i + 2); newret += ldexp(yn / (real)(2 * i + 3), -i - 2);
if (newret == ret) if (newret == ret)
break; break;
ret = newret; ret = newret;
@@ -1182,19 +1156,19 @@ real atan(real const &x)
} }
else if (absx < real::R_2) else if (absx < real::R_2)
{ {
real y = (absx - real::R_SQRT3) >> 1; real y = (absx - real::R_SQRT3) / 2;
real yn = y, my2 = -y * y; real yn = y, my2 = -y * y;
for (int i = 1; ; i += 6) for (int i = 1; ; i += 6)
{ {
real newret = ret + ((yn / (real)i) >> 1); real newret = ret + ((yn / (real)i) / 2);
yn *= y; yn *= y;
newret -= (real::R_SQRT3 >> 1) * yn / (real)(i + 1); newret -= (real::R_SQRT3 / 2) * yn / (real)(i + 1);
yn *= y; yn *= y;
newret += yn / (real)(i + 2); newret += yn / (real)(i + 2);
yn *= y; yn *= y;
newret -= (real::R_SQRT3 >> 1) * yn / (real)(i + 3); newret -= (real::R_SQRT3 / 2) * yn / (real)(i + 3);
yn *= y; yn *= y;
newret += (yn / (real)(i + 4)) >> 1; newret += (yn / (real)(i + 4)) / 2;
if (newret == ret) if (newret == ret)
break; break;
ret = newret; ret = newret;
@@ -1329,15 +1303,15 @@ 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_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 / 2;
real const real::R_PI_3 = R_PI / R_3; real const real::R_PI_3 = R_PI / R_3;
real const real::R_PI_4 = R_PI >> 2; real const real::R_PI_4 = R_PI / 4;
real const real::R_1_PI = re(R_PI); real const real::R_1_PI = re(R_PI);
real const real::R_2_PI = R_1_PI << 1; real const real::R_2_PI = R_1_PI * 2;
real const real::R_2_SQRTPI = re(sqrt(R_PI)) << 1; real const real::R_2_SQRTPI = re(sqrt(R_PI)) * 2;
real const real::R_SQRT2 = sqrt(R_2); real const real::R_SQRT2 = sqrt(R_2);
real const real::R_SQRT3 = sqrt(R_3); real const real::R_SQRT3 = sqrt(R_3);
real const real::R_SQRT1_2 = R_SQRT2 >> 1; real const real::R_SQRT1_2 = R_SQRT2 / 2;


} /* namespace lol */ } /* namespace lol */



+ 8
- 14
test/unit/real.cpp 查看文件

@@ -221,31 +221,25 @@ LOLUNIT_FIXTURE(RealTest)
/* 1 / 3 * 3 should be close to 1... check that it does not differ /* 1 / 3 * 3 should be close to 1... check that it does not differ
* by more than 2^-k where k is the number of bits in the mantissa. */ * by more than 2^-k where k is the number of bits in the mantissa. */
real a = real::R_1 / real::R_3 * real::R_3; real a = real::R_1 / real::R_3 * real::R_3;
real b = (real::R_1 - a) << (real::BIGITS * real::BIGIT_BITS); real b = ldexp(real::R_1 - a, real::BIGITS * real::BIGIT_BITS);


LOLUNIT_ASSERT_LEQUAL((double)fabs(b), 1.0); LOLUNIT_ASSERT_LEQUAL((double)fabs(b), 1.0);
} }


LOLUNIT_TEST(Shift) LOLUNIT_TEST(LoadExp)
{ {
real a1(1.5); real a1(1.5);
real a2(-1.5); real a2(-1.5);
real a3(0.0); real a3(0.0);


LOLUNIT_ASSERT_EQUAL((double)(a1 >> 7), 0.01171875); LOLUNIT_ASSERT_EQUAL((double)ldexp(a1, 7), 192.0);
LOLUNIT_ASSERT_EQUAL((double)(a1 >> -7), 192.0); LOLUNIT_ASSERT_EQUAL((double)ldexp(a1, -7), 0.01171875);
LOLUNIT_ASSERT_EQUAL((double)(a1 << 7), 192.0);
LOLUNIT_ASSERT_EQUAL((double)(a1 << -7), 0.01171875);


LOLUNIT_ASSERT_EQUAL((double)(a2 >> 7), -0.01171875); LOLUNIT_ASSERT_EQUAL((double)ldexp(a2, 7), -192.0);
LOLUNIT_ASSERT_EQUAL((double)(a2 >> -7), -192.0); LOLUNIT_ASSERT_EQUAL((double)ldexp(a2, -7), -0.01171875);
LOLUNIT_ASSERT_EQUAL((double)(a2 << 7), -192.0);
LOLUNIT_ASSERT_EQUAL((double)(a2 << -7), -0.01171875);


LOLUNIT_ASSERT_EQUAL((double)(a3 >> 7), 0.0); LOLUNIT_ASSERT_EQUAL((double)ldexp(a3, 7), 0.0);
LOLUNIT_ASSERT_EQUAL((double)(a3 >> -7), 0.0); LOLUNIT_ASSERT_EQUAL((double)ldexp(a3, -7), 0.0);
LOLUNIT_ASSERT_EQUAL((double)(a3 << 7), 0.0);
LOLUNIT_ASSERT_EQUAL((double)(a3 << -7), 0.0);
} }


LOLUNIT_TEST(Bool) LOLUNIT_TEST(Bool)


||||||
x
 
000:0
Loading…
取消
儲存