Chris@16: // Boost.Polygon library detail/voronoi_robust_fpt.hpp header file Chris@16: Chris@16: // Copyright Andrii Sydorchuk 2010-2012. Chris@16: // Distributed under the Boost Software License, Version 1.0. Chris@16: // (See accompanying file LICENSE_1_0.txt or copy at Chris@16: // http://www.boost.org/LICENSE_1_0.txt) Chris@16: Chris@16: // See http://www.boost.org for updates, documentation, and revision history. Chris@16: Chris@16: #ifndef BOOST_POLYGON_DETAIL_VORONOI_ROBUST_FPT Chris@16: #define BOOST_POLYGON_DETAIL_VORONOI_ROBUST_FPT Chris@16: Chris@101: #include Chris@16: #include Chris@16: Chris@16: // Geometry predicates with floating-point variables usually require Chris@16: // high-precision predicates to retrieve the correct result. Chris@16: // Epsilon robust predicates give the result within some epsilon relative Chris@16: // error, but are a lot faster than high-precision predicates. Chris@16: // To make algorithm robust and efficient epsilon robust predicates are Chris@16: // used at the first step. In case of the undefined result high-precision Chris@16: // arithmetic is used to produce required robustness. This approach Chris@16: // requires exact computation of epsilon intervals within which epsilon Chris@16: // robust predicates have undefined value. Chris@16: // There are two ways to measure an error of floating-point calculations: Chris@16: // relative error and ULPs (units in the last place). Chris@16: // Let EPS be machine epsilon, then next inequalities have place: Chris@16: // 1 EPS <= 1 ULP <= 2 EPS (1), 0.5 ULP <= 1 EPS <= 1 ULP (2). Chris@16: // ULPs are good for measuring rounding errors and comparing values. Chris@16: // Relative errors are good for computation of general relative Chris@16: // error of formulas or expressions. So to calculate epsilon Chris@16: // interval within which epsilon robust predicates have undefined result Chris@16: // next schema is used: Chris@16: // 1) Compute rounding errors of initial variables using ULPs; Chris@16: // 2) Transform ULPs to epsilons using upper bound of the (1); Chris@16: // 3) Compute relative error of the formula using epsilon arithmetic; Chris@16: // 4) Transform epsilon to ULPs using upper bound of the (2); Chris@16: // In case two values are inside undefined ULP range use high-precision Chris@16: // arithmetic to produce the correct result, else output the result. Chris@16: // Look at almost_equal function to see how two floating-point variables Chris@16: // are checked to fit in the ULP range. Chris@16: // If A has relative error of r(A) and B has relative error of r(B) then: Chris@16: // 1) r(A + B) <= max(r(A), r(B)), for A * B >= 0; Chris@16: // 2) r(A - B) <= B*r(A)+A*r(B)/(A-B), for A * B >= 0; Chris@16: // 2) r(A * B) <= r(A) + r(B); Chris@16: // 3) r(A / B) <= r(A) + r(B); Chris@16: // In addition rounding error should be added, that is always equal to Chris@16: // 0.5 ULP or at most 1 epsilon. As you might see from the above formulas Chris@16: // subtraction relative error may be extremely large, that's why Chris@16: // epsilon robust comparator class is used to store floating point values Chris@16: // and compute subtraction as the final step of the evaluation. Chris@16: // For further information about relative errors and ULPs try this link: Chris@16: // http://docs.sun.com/source/806-3568/ncg_goldberg.html Chris@16: Chris@16: namespace boost { Chris@16: namespace polygon { Chris@16: namespace detail { Chris@16: Chris@16: template Chris@16: T get_sqrt(const T& that) { Chris@16: return (std::sqrt)(that); Chris@16: } Chris@16: Chris@16: template Chris@16: bool is_pos(const T& that) { Chris@16: return that > 0; Chris@16: } Chris@16: Chris@16: template Chris@16: bool is_neg(const T& that) { Chris@16: return that < 0; Chris@16: } Chris@16: Chris@16: template Chris@16: bool is_zero(const T& that) { Chris@16: return that == 0; Chris@16: } Chris@16: Chris@16: template Chris@16: class robust_fpt { Chris@16: public: Chris@16: typedef _fpt floating_point_type; Chris@16: typedef _fpt relative_error_type; Chris@16: Chris@16: // Rounding error is at most 1 EPS. Chris@16: enum { Chris@16: ROUNDING_ERROR = 1 Chris@16: }; Chris@16: Chris@16: robust_fpt() : fpv_(0.0), re_(0.0) {} Chris@16: explicit robust_fpt(floating_point_type fpv) : Chris@16: fpv_(fpv), re_(0.0) {} Chris@16: robust_fpt(floating_point_type fpv, relative_error_type error) : Chris@16: fpv_(fpv), re_(error) {} Chris@16: Chris@16: floating_point_type fpv() const { return fpv_; } Chris@16: relative_error_type re() const { return re_; } Chris@16: relative_error_type ulp() const { return re_; } Chris@16: Chris@16: robust_fpt& operator=(const robust_fpt& that) { Chris@16: this->fpv_ = that.fpv_; Chris@16: this->re_ = that.re_; Chris@16: return *this; Chris@16: } Chris@16: Chris@16: bool has_pos_value() const { Chris@16: return is_pos(fpv_); Chris@16: } Chris@16: Chris@16: bool has_neg_value() const { Chris@16: return is_neg(fpv_); Chris@16: } Chris@16: Chris@16: bool has_zero_value() const { Chris@16: return is_zero(fpv_); Chris@16: } Chris@16: Chris@16: robust_fpt operator-() const { Chris@16: return robust_fpt(-fpv_, re_); Chris@16: } Chris@16: Chris@16: robust_fpt& operator+=(const robust_fpt& that) { Chris@16: floating_point_type fpv = this->fpv_ + that.fpv_; Chris@16: if ((!is_neg(this->fpv_) && !is_neg(that.fpv_)) || Chris@16: (!is_pos(this->fpv_) && !is_pos(that.fpv_))) { Chris@16: this->re_ = (std::max)(this->re_, that.re_) + ROUNDING_ERROR; Chris@16: } else { Chris@16: floating_point_type temp = Chris@16: (this->fpv_ * this->re_ - that.fpv_ * that.re_) / fpv; Chris@16: if (is_neg(temp)) Chris@16: temp = -temp; Chris@16: this->re_ = temp + ROUNDING_ERROR; Chris@16: } Chris@16: this->fpv_ = fpv; Chris@16: return *this; Chris@16: } Chris@16: Chris@16: robust_fpt& operator-=(const robust_fpt& that) { Chris@16: floating_point_type fpv = this->fpv_ - that.fpv_; Chris@16: if ((!is_neg(this->fpv_) && !is_pos(that.fpv_)) || Chris@16: (!is_pos(this->fpv_) && !is_neg(that.fpv_))) { Chris@16: this->re_ = (std::max)(this->re_, that.re_) + ROUNDING_ERROR; Chris@16: } else { Chris@16: floating_point_type temp = Chris@16: (this->fpv_ * this->re_ + that.fpv_ * that.re_) / fpv; Chris@16: if (is_neg(temp)) Chris@16: temp = -temp; Chris@16: this->re_ = temp + ROUNDING_ERROR; Chris@16: } Chris@16: this->fpv_ = fpv; Chris@16: return *this; Chris@16: } Chris@16: Chris@16: robust_fpt& operator*=(const robust_fpt& that) { Chris@16: this->re_ += that.re_ + ROUNDING_ERROR; Chris@16: this->fpv_ *= that.fpv_; Chris@16: return *this; Chris@16: } Chris@16: Chris@16: robust_fpt& operator/=(const robust_fpt& that) { Chris@16: this->re_ += that.re_ + ROUNDING_ERROR; Chris@16: this->fpv_ /= that.fpv_; Chris@16: return *this; Chris@16: } Chris@16: Chris@16: robust_fpt operator+(const robust_fpt& that) const { Chris@16: floating_point_type fpv = this->fpv_ + that.fpv_; Chris@16: relative_error_type re; Chris@16: if ((!is_neg(this->fpv_) && !is_neg(that.fpv_)) || Chris@16: (!is_pos(this->fpv_) && !is_pos(that.fpv_))) { Chris@16: re = (std::max)(this->re_, that.re_) + ROUNDING_ERROR; Chris@16: } else { Chris@16: floating_point_type temp = Chris@16: (this->fpv_ * this->re_ - that.fpv_ * that.re_) / fpv; Chris@16: if (is_neg(temp)) Chris@16: temp = -temp; Chris@16: re = temp + ROUNDING_ERROR; Chris@16: } Chris@16: return robust_fpt(fpv, re); Chris@16: } Chris@16: Chris@16: robust_fpt operator-(const robust_fpt& that) const { Chris@16: floating_point_type fpv = this->fpv_ - that.fpv_; Chris@16: relative_error_type re; Chris@16: if ((!is_neg(this->fpv_) && !is_pos(that.fpv_)) || Chris@16: (!is_pos(this->fpv_) && !is_neg(that.fpv_))) { Chris@16: re = (std::max)(this->re_, that.re_) + ROUNDING_ERROR; Chris@16: } else { Chris@16: floating_point_type temp = Chris@16: (this->fpv_ * this->re_ + that.fpv_ * that.re_) / fpv; Chris@16: if (is_neg(temp)) Chris@16: temp = -temp; Chris@16: re = temp + ROUNDING_ERROR; Chris@16: } Chris@16: return robust_fpt(fpv, re); Chris@16: } Chris@16: Chris@16: robust_fpt operator*(const robust_fpt& that) const { Chris@16: floating_point_type fpv = this->fpv_ * that.fpv_; Chris@16: relative_error_type re = this->re_ + that.re_ + ROUNDING_ERROR; Chris@16: return robust_fpt(fpv, re); Chris@16: } Chris@16: Chris@16: robust_fpt operator/(const robust_fpt& that) const { Chris@16: floating_point_type fpv = this->fpv_ / that.fpv_; Chris@16: relative_error_type re = this->re_ + that.re_ + ROUNDING_ERROR; Chris@16: return robust_fpt(fpv, re); Chris@16: } Chris@16: Chris@16: robust_fpt sqrt() const { Chris@16: return robust_fpt(get_sqrt(fpv_), Chris@16: re_ * static_cast(0.5) + Chris@16: ROUNDING_ERROR); Chris@16: } Chris@16: Chris@16: private: Chris@16: floating_point_type fpv_; Chris@16: relative_error_type re_; Chris@16: }; Chris@16: Chris@16: template Chris@16: robust_fpt get_sqrt(const robust_fpt& that) { Chris@16: return that.sqrt(); Chris@16: } Chris@16: Chris@16: template Chris@16: bool is_pos(const robust_fpt& that) { Chris@16: return that.has_pos_value(); Chris@16: } Chris@16: Chris@16: template Chris@16: bool is_neg(const robust_fpt& that) { Chris@16: return that.has_neg_value(); Chris@16: } Chris@16: Chris@16: template Chris@16: bool is_zero(const robust_fpt& that) { Chris@16: return that.has_zero_value(); Chris@16: } Chris@16: Chris@16: // robust_dif consists of two not negative values: value1 and value2. Chris@16: // The resulting expression is equal to the value1 - value2. Chris@16: // Subtraction of a positive value is equivalent to the addition to value2 Chris@16: // and subtraction of a negative value is equivalent to the addition to Chris@16: // value1. The structure implicitly avoids difference computation. Chris@16: template Chris@16: class robust_dif { Chris@16: public: Chris@16: robust_dif() : Chris@16: positive_sum_(0), Chris@16: negative_sum_(0) {} Chris@16: Chris@16: explicit robust_dif(const T& value) : Chris@16: positive_sum_((value > 0)?value:0), Chris@16: negative_sum_((value < 0)?-value:0) {} Chris@16: Chris@16: robust_dif(const T& pos, const T& neg) : Chris@16: positive_sum_(pos), Chris@16: negative_sum_(neg) {} Chris@16: Chris@16: T dif() const { Chris@16: return positive_sum_ - negative_sum_; Chris@16: } Chris@16: Chris@16: T pos() const { Chris@16: return positive_sum_; Chris@16: } Chris@16: Chris@16: T neg() const { Chris@16: return negative_sum_; Chris@16: } Chris@16: Chris@16: robust_dif operator-() const { Chris@16: return robust_dif(negative_sum_, positive_sum_); Chris@16: } Chris@16: Chris@16: robust_dif& operator+=(const T& val) { Chris@16: if (!is_neg(val)) Chris@16: positive_sum_ += val; Chris@16: else Chris@16: negative_sum_ -= val; Chris@16: return *this; Chris@16: } Chris@16: Chris@16: robust_dif& operator+=(const robust_dif& that) { Chris@16: positive_sum_ += that.positive_sum_; Chris@16: negative_sum_ += that.negative_sum_; Chris@16: return *this; Chris@16: } Chris@16: Chris@16: robust_dif& operator-=(const T& val) { Chris@16: if (!is_neg(val)) Chris@16: negative_sum_ += val; Chris@16: else Chris@16: positive_sum_ -= val; Chris@16: return *this; Chris@16: } Chris@16: Chris@16: robust_dif& operator-=(const robust_dif& that) { Chris@16: positive_sum_ += that.negative_sum_; Chris@16: negative_sum_ += that.positive_sum_; Chris@16: return *this; Chris@16: } Chris@16: Chris@16: robust_dif& operator*=(const T& val) { Chris@16: if (!is_neg(val)) { Chris@16: positive_sum_ *= val; Chris@16: negative_sum_ *= val; Chris@16: } else { Chris@16: positive_sum_ *= -val; Chris@16: negative_sum_ *= -val; Chris@16: swap(); Chris@16: } Chris@16: return *this; Chris@16: } Chris@16: Chris@16: robust_dif& operator*=(const robust_dif& that) { Chris@16: T positive_sum = this->positive_sum_ * that.positive_sum_ + Chris@16: this->negative_sum_ * that.negative_sum_; Chris@16: T negative_sum = this->positive_sum_ * that.negative_sum_ + Chris@16: this->negative_sum_ * that.positive_sum_; Chris@16: positive_sum_ = positive_sum; Chris@16: negative_sum_ = negative_sum; Chris@16: return *this; Chris@16: } Chris@16: Chris@16: robust_dif& operator/=(const T& val) { Chris@16: if (!is_neg(val)) { Chris@16: positive_sum_ /= val; Chris@16: negative_sum_ /= val; Chris@16: } else { Chris@16: positive_sum_ /= -val; Chris@16: negative_sum_ /= -val; Chris@16: swap(); Chris@16: } Chris@16: return *this; Chris@16: } Chris@16: Chris@16: private: Chris@16: void swap() { Chris@16: (std::swap)(positive_sum_, negative_sum_); Chris@16: } Chris@16: Chris@16: T positive_sum_; Chris@16: T negative_sum_; Chris@16: }; Chris@16: Chris@16: template Chris@16: robust_dif operator+(const robust_dif& lhs, Chris@16: const robust_dif& rhs) { Chris@16: return robust_dif(lhs.pos() + rhs.pos(), lhs.neg() + rhs.neg()); Chris@16: } Chris@16: Chris@16: template Chris@16: robust_dif operator+(const robust_dif& lhs, const T& rhs) { Chris@16: if (!is_neg(rhs)) { Chris@16: return robust_dif(lhs.pos() + rhs, lhs.neg()); Chris@16: } else { Chris@16: return robust_dif(lhs.pos(), lhs.neg() - rhs); Chris@16: } Chris@16: } Chris@16: Chris@16: template Chris@16: robust_dif operator+(const T& lhs, const robust_dif& rhs) { Chris@16: if (!is_neg(lhs)) { Chris@16: return robust_dif(lhs + rhs.pos(), rhs.neg()); Chris@16: } else { Chris@16: return robust_dif(rhs.pos(), rhs.neg() - lhs); Chris@16: } Chris@16: } Chris@16: Chris@16: template Chris@16: robust_dif operator-(const robust_dif& lhs, Chris@16: const robust_dif& rhs) { Chris@16: return robust_dif(lhs.pos() + rhs.neg(), lhs.neg() + rhs.pos()); Chris@16: } Chris@16: Chris@16: template Chris@16: robust_dif operator-(const robust_dif& lhs, const T& rhs) { Chris@16: if (!is_neg(rhs)) { Chris@16: return robust_dif(lhs.pos(), lhs.neg() + rhs); Chris@16: } else { Chris@16: return robust_dif(lhs.pos() - rhs, lhs.neg()); Chris@16: } Chris@16: } Chris@16: Chris@16: template Chris@16: robust_dif operator-(const T& lhs, const robust_dif& rhs) { Chris@16: if (!is_neg(lhs)) { Chris@16: return robust_dif(lhs + rhs.neg(), rhs.pos()); Chris@16: } else { Chris@16: return robust_dif(rhs.neg(), rhs.pos() - lhs); Chris@16: } Chris@16: } Chris@16: Chris@16: template Chris@16: robust_dif operator*(const robust_dif& lhs, Chris@16: const robust_dif& rhs) { Chris@16: T res_pos = lhs.pos() * rhs.pos() + lhs.neg() * rhs.neg(); Chris@16: T res_neg = lhs.pos() * rhs.neg() + lhs.neg() * rhs.pos(); Chris@16: return robust_dif(res_pos, res_neg); Chris@16: } Chris@16: Chris@16: template Chris@16: robust_dif operator*(const robust_dif& lhs, const T& val) { Chris@16: if (!is_neg(val)) { Chris@16: return robust_dif(lhs.pos() * val, lhs.neg() * val); Chris@16: } else { Chris@16: return robust_dif(-lhs.neg() * val, -lhs.pos() * val); Chris@16: } Chris@16: } Chris@16: Chris@16: template Chris@16: robust_dif operator*(const T& val, const robust_dif& rhs) { Chris@16: if (!is_neg(val)) { Chris@16: return robust_dif(val * rhs.pos(), val * rhs.neg()); Chris@16: } else { Chris@16: return robust_dif(-val * rhs.neg(), -val * rhs.pos()); Chris@16: } Chris@16: } Chris@16: Chris@16: template Chris@16: robust_dif operator/(const robust_dif& lhs, const T& val) { Chris@16: if (!is_neg(val)) { Chris@16: return robust_dif(lhs.pos() / val, lhs.neg() / val); Chris@16: } else { Chris@16: return robust_dif(-lhs.neg() / val, -lhs.pos() / val); Chris@16: } Chris@16: } Chris@16: Chris@16: // Used to compute expressions that operate with sqrts with predefined Chris@16: // relative error. Evaluates expressions of the next type: Chris@16: // sum(i = 1 .. n)(A[i] * sqrt(B[i])), 1 <= n <= 4. Chris@16: template Chris@16: class robust_sqrt_expr { Chris@16: public: Chris@16: enum MAX_RELATIVE_ERROR { Chris@16: MAX_RELATIVE_ERROR_EVAL1 = 4, Chris@16: MAX_RELATIVE_ERROR_EVAL2 = 7, Chris@16: MAX_RELATIVE_ERROR_EVAL3 = 16, Chris@16: MAX_RELATIVE_ERROR_EVAL4 = 25 Chris@16: }; Chris@16: Chris@16: // Evaluates expression (re = 4 EPS): Chris@16: // A[0] * sqrt(B[0]). Chris@16: _fpt eval1(_int* A, _int* B) { Chris@16: _fpt a = convert(A[0]); Chris@16: _fpt b = convert(B[0]); Chris@16: return a * get_sqrt(b); Chris@16: } Chris@16: Chris@16: // Evaluates expression (re = 7 EPS): Chris@16: // A[0] * sqrt(B[0]) + A[1] * sqrt(B[1]). Chris@16: _fpt eval2(_int* A, _int* B) { Chris@16: _fpt a = eval1(A, B); Chris@16: _fpt b = eval1(A + 1, B + 1); Chris@16: if ((!is_neg(a) && !is_neg(b)) || Chris@16: (!is_pos(a) && !is_pos(b))) Chris@16: return a + b; Chris@16: return convert(A[0] * A[0] * B[0] - A[1] * A[1] * B[1]) / (a - b); Chris@16: } Chris@16: Chris@16: // Evaluates expression (re = 16 EPS): Chris@16: // A[0] * sqrt(B[0]) + A[1] * sqrt(B[1]) + A[2] * sqrt(B[2]). Chris@16: _fpt eval3(_int* A, _int* B) { Chris@16: _fpt a = eval2(A, B); Chris@16: _fpt b = eval1(A + 2, B + 2); Chris@16: if ((!is_neg(a) && !is_neg(b)) || Chris@16: (!is_pos(a) && !is_pos(b))) Chris@16: return a + b; Chris@16: tA[3] = A[0] * A[0] * B[0] + A[1] * A[1] * B[1] - A[2] * A[2] * B[2]; Chris@16: tB[3] = 1; Chris@16: tA[4] = A[0] * A[1] * 2; Chris@16: tB[4] = B[0] * B[1]; Chris@16: return eval2(tA + 3, tB + 3) / (a - b); Chris@16: } Chris@16: Chris@16: Chris@16: // Evaluates expression (re = 25 EPS): Chris@16: // A[0] * sqrt(B[0]) + A[1] * sqrt(B[1]) + Chris@16: // A[2] * sqrt(B[2]) + A[3] * sqrt(B[3]). Chris@16: _fpt eval4(_int* A, _int* B) { Chris@16: _fpt a = eval2(A, B); Chris@16: _fpt b = eval2(A + 2, B + 2); Chris@16: if ((!is_neg(a) && !is_neg(b)) || Chris@16: (!is_pos(a) && !is_pos(b))) Chris@16: return a + b; Chris@16: tA[0] = A[0] * A[0] * B[0] + A[1] * A[1] * B[1] - Chris@16: A[2] * A[2] * B[2] - A[3] * A[3] * B[3]; Chris@16: tB[0] = 1; Chris@16: tA[1] = A[0] * A[1] * 2; Chris@16: tB[1] = B[0] * B[1]; Chris@16: tA[2] = A[2] * A[3] * -2; Chris@16: tB[2] = B[2] * B[3]; Chris@16: return eval3(tA, tB) / (a - b); Chris@16: } Chris@16: Chris@16: private: Chris@16: _int tA[5]; Chris@16: _int tB[5]; Chris@16: _converter convert; Chris@16: }; Chris@16: } // detail Chris@16: } // polygon Chris@16: } // boost Chris@16: Chris@16: #endif // BOOST_POLYGON_DETAIL_VORONOI_ROBUST_FPT