// Released into the public-domain by Robert Bridson, 2009. // Modified by Tyson Brochu, 2011. #include "expansion.h" #include #include #include namespace CCD { namespace { //============================================================================== void two_sum(double a, double b, double& x, double& y) { x=a+b; double z=x-a; y=(a-(x-z))+(b-z); } //============================================================================== // requires that |a|>=|b| void fast_two_sum(double a, double b, double& x, double& y) { assert( a == a && b == b ); assert(std::fabs(a)>=std::fabs(b)); x=a+b; y=(a-x)+b; } //============================================================================== void split(double a, double& x, double& y) { double c=134217729*a; x=c-(c-a); y=a-x; } //============================================================================== void two_product(double a, double b, double& x, double& y) { x=a*b; double a1, a2, b1, b2; split(a, a1, a2); split(b, b1, b2); y=a2*b2-(((x-a1*b1)-a2*b1)-a1*b2); } } // namespace //============================================================================== bool is_zero( const expansion& a ) { return ( a.v.size() == 0 ); } //============================================================================== int sign( const expansion& a ) { if ( a.v.size() == 0 ) { return 0; } // REVIEW: I'm assuming we can get the sign of the expansion by the sign of its leading term (i.e. the sum of all other terms < leading term ) // This true if the expansion if increasing and nonoverlapping if ( a.v.back() > 0 ) { return 1; } return -1; } //============================================================================== void add(double a, double b, expansion& sum) { sum.resize(2); two_sum(a, b, sum.v[1], sum.v[0]); } //============================================================================== // a and sum may be aliased to the same expansion for in-place addition void add(const expansion& a, double b, expansion& sum) { size_t m=a.v.size(); sum.v.reserve(m+1); double s; for(size_t i=0; i= 0; --i ) { double new_q, small_q; fast_two_sum( q, e.v[i], new_q, small_q ); if ( small_q != 0 ) { g.v[bottom--] = new_q; q = small_q; } else { q = new_q; } } g.v[bottom] = q; h.v.resize( e.v.size(), 0 ); unsigned int top = 0; for ( size_t i = bottom+1; i < e.v.size(); ++i ) { double new_q, small_q; fast_two_sum( g.v[i], q, new_q, small_q ); if ( small_q != 0 ) { h.v[top++] = small_q; } q = new_q; } h.v[top] = q; h.resize( top+1 ); } //============================================================================== bool divide( const expansion& x, const expansion& y, expansion& q ) { assert( !is_zero( y ) ); if ( is_zero( x ) ) { // 0 / y = 0 make_expansion( 0, q ); return true; } const double divisor = estimate(y); // q is the quotient, built by repeatedly dividing the remainder // Initially, q = estimate(x) / estimate(y) make_expansion( estimate(x) / divisor, q ); expansion qy; multiply( q, y, qy ); expansion r; subtract( x, qy, r ); while ( !is_zero(r) ) { // s is the next term in the quotient q: // s = estimate(r) / estimate(y) expansion s; make_expansion( estimate(r) / divisor, s ); if ( is_zero(s) ) { assert ( !is_zero(y) ); std::cout << "underflow, s == 0" << std::endl; std::cout << "divisor: " << divisor << std::endl; return false; } // q += s add( q, s, q ); // r -= s*y expansion sy; multiply( s, y, sy ); // underflow, quotient not representable by an expansion if ( is_zero(sy) ) { assert ( !is_zero(s) && !is_zero(y) ); std::cout << "underflow, sy == 0" << std::endl; return false; } subtract( r, sy, r ); expansion compressed_r; compress( r, compressed_r ); r = compressed_r; } remove_zeros( q ); return true; } //============================================================================== void remove_zeros(expansion& a) { unsigned int i, j; for ( i = 0, j = 0; i < a.v.size(); ++i ) { if ( a.v[i] ) { a.v[j++] = a.v[i]; } } a.resize(j); } //============================================================================== double estimate(const expansion& a) { double x=0; for(unsigned int i=0; i