mirror of https://github.com/CGAL/cgal
Add divides() function, and detect cases where exact_division()
is called when the division is not exact. Also, rescale intermediate remainders.
This commit is contained in:
parent
c1bb538eb0
commit
c33e87a838
|
|
@ -61,9 +61,12 @@ or \ccc{SqrtFieldNumberType} if \ccc{CGAL_MP_FLOAT_ALLOW_INEXACT} is set.
|
||||||
\ccFunction{std::istream& operator>>(std::istream& in, MP_Float& m);}
|
\ccFunction{std::istream& operator>>(std::istream& in, MP_Float& m);}
|
||||||
{reads a \ccc{double} from \ccc{in}, then converts it to an \ccc{MP_Float}.}
|
{reads a \ccc{double} from \ccc{in}, then converts it to an \ccc{MP_Float}.}
|
||||||
|
|
||||||
|
\ccFunction{bool divides(const MP_Float &a, const MP_Float &b);}
|
||||||
|
{returns true iff \ccc{b} divides \ccc{b}.}
|
||||||
|
|
||||||
\ccFunction{MP_Float exact_division(const MP_Float &a, const MP_Float &b);}
|
\ccFunction{MP_Float exact_division(const MP_Float &a, const MP_Float &b);}
|
||||||
{computes the result of the division of \ccc{a} by \ccc{b}.
|
{computes the result of the division of \ccc{a} by \ccc{b}.
|
||||||
\ccPrecond{\ccc{b} exactly divides \ccc{a}}.}
|
\ccPrecond{divides(a, b)}.}
|
||||||
|
|
||||||
\ccFunction{MP_Float approximate_division(const MP_Float &a, const MP_Float &b);}
|
\ccFunction{MP_Float approximate_division(const MP_Float &a, const MP_Float &b);}
|
||||||
{computes an approximation of the division by converting the operands to
|
{computes an approximation of the division by converting the operands to
|
||||||
|
|
|
||||||
|
|
@ -491,36 +491,79 @@ operator>> (std::istream & is, MP_Float &b);
|
||||||
// TODO : needs function "bool divides(n, d)" for validity checking, and maybe other things.
|
// TODO : needs function "bool divides(n, d)" for validity checking, and maybe other things.
|
||||||
// TODO : needs function div(), with remainder.
|
// TODO : needs function div(), with remainder.
|
||||||
|
|
||||||
|
namespace CGALi {
|
||||||
|
|
||||||
inline // Move it to libCGAL once it's stable.
|
inline // Move it to libCGAL once it's stable.
|
||||||
MP_Float
|
MP_Float
|
||||||
exact_division(MP_Float n, MP_Float d)
|
exact_division_internal(MP_Float n, MP_Float d, bool & divides)
|
||||||
{
|
{
|
||||||
CGAL_assertion(d != 0);
|
CGAL_assertion(d != 0);
|
||||||
|
|
||||||
|
typedef MP_Float::exponent_type exponent_type;
|
||||||
// Rescale them to have to_double() values with reasonnable exponents.
|
// Rescale them to have to_double() values with reasonnable exponents.
|
||||||
MP_Float::exponent_type scale_n = n.find_scale();
|
exponent_type scale_n = n.find_scale();
|
||||||
MP_Float::exponent_type scale_d = d.find_scale();
|
exponent_type scale_d = d.find_scale();
|
||||||
n.rescale(scale_n);
|
n.rescale(scale_n);
|
||||||
d.rescale(scale_d);
|
d.rescale(scale_d);
|
||||||
|
|
||||||
|
// A simple criteria for detecting when the division is not exact
|
||||||
|
// is that if it is exact, then the result must have smaller or
|
||||||
|
// equal bit length than "n".
|
||||||
|
// Which we approximate with size() and a confortable margin.
|
||||||
|
exponent_type max_size_if_exact = n.size() + d.size() + 200;
|
||||||
|
|
||||||
// School division algorithm.
|
// School division algorithm.
|
||||||
MP_Float res = to_double(n) / to_double(d);
|
MP_Float res = to_double(n) / to_double(d);
|
||||||
MP_Float remainder = n - res * d;
|
MP_Float remainder = n - res * d;
|
||||||
while ( remainder != 0 )
|
while ( remainder != 0 )
|
||||||
{
|
{
|
||||||
|
// We also have to rescale here, since remainder can diminish towards 0.
|
||||||
|
exponent_type scale_rem = remainder.find_scale();
|
||||||
|
remainder.rescale(scale_rem);
|
||||||
|
res.rescale(scale_rem);
|
||||||
|
scale_n += scale_rem;
|
||||||
|
|
||||||
|
// A double approximation of the quotient
|
||||||
|
// (imagine school division with base ~2^53).
|
||||||
double approx = to_double(remainder) / to_double(d);
|
double approx = to_double(remainder) / to_double(d);
|
||||||
CGAL_assertion(approx != 0);
|
CGAL_assertion(approx != 0);
|
||||||
approx = (approx + (4*approx)) - (4*approx); // chop-off the last bit.
|
approx = (approx + (4*approx)) - (4*approx); // chop-off the last bit.
|
||||||
res += approx;
|
res += approx;
|
||||||
remainder -= approx * d;
|
remainder -= approx * d;
|
||||||
// TODO : add detection when the division is not exact (abort).
|
if (res.size() > max_size_if_exact)
|
||||||
|
{
|
||||||
|
divides = false;
|
||||||
|
return MP_Float();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
divides = true;
|
||||||
// Scale back the result.
|
// Scale back the result.
|
||||||
res.rescale(scale_d - scale_n);
|
res.rescale(scale_d - scale_n);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace CGALi
|
||||||
|
|
||||||
|
inline // Move it to libCGAL once it's stable.
|
||||||
|
MP_Float
|
||||||
|
exact_division(const MP_Float & n, const MP_Float & d)
|
||||||
|
{
|
||||||
|
bool exact_div;
|
||||||
|
MP_Float res = CGALi::exact_division_internal(n, d, exact_div);
|
||||||
|
CGAL_assertion_msg(exact_div, "exact_division() called with operands which do not divide");
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline // Move it to libCGAL once it's stable.
|
||||||
|
bool
|
||||||
|
divides(const MP_Float & n, const MP_Float & d)
|
||||||
|
{
|
||||||
|
bool exact_div;
|
||||||
|
CGALi::exact_division_internal(n, d, exact_div);
|
||||||
|
return exact_div;
|
||||||
|
}
|
||||||
|
|
||||||
CGAL_END_NAMESPACE
|
CGAL_END_NAMESPACE
|
||||||
|
|
||||||
#endif // CGAL_MP_FLOAT_H
|
#endif // CGAL_MP_FLOAT_H
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Sylvain Pion.
|
// Sylvain Pion.
|
||||||
|
|
||||||
#include <CGAL/basic.h>
|
#include <CGAL/basic.h>
|
||||||
|
#include <CGAL/exceptions.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
@ -21,6 +22,8 @@ void test_exact_division()
|
||||||
|
|
||||||
// Let's pick 2 random values, multiply them, divide and check.
|
// Let's pick 2 random values, multiply them, divide and check.
|
||||||
|
|
||||||
|
MPF tmp = exact_division(MPF(0), MPF(1));
|
||||||
|
|
||||||
for (int i = 0; i < 100000; ++i) {
|
for (int i = 0; i < 100000; ++i) {
|
||||||
MPF d = CGAL::default_random.get_double();
|
MPF d = CGAL::default_random.get_double();
|
||||||
MPF n = CGAL::default_random.get_double();
|
MPF n = CGAL::default_random.get_double();
|
||||||
|
|
@ -35,6 +38,10 @@ void test_exact_division()
|
||||||
if ((i%3) < 1) n *= CGAL::default_random.get_double();
|
if ((i%3) < 1) n *= CGAL::default_random.get_double();
|
||||||
if ((i%3) < 2) n *= CGAL::default_random.get_double();
|
if ((i%3) < 2) n *= CGAL::default_random.get_double();
|
||||||
|
|
||||||
|
// Try to change the signs
|
||||||
|
if ((i%7) == 0) d = -d;
|
||||||
|
if ((i%11) == 0) n = -n;
|
||||||
|
|
||||||
// Scale both numerator and denominator to test overflow/underflow
|
// Scale both numerator and denominator to test overflow/underflow
|
||||||
// situations.
|
// situations.
|
||||||
d.rescale(107 * ((i%19) - 10));
|
d.rescale(107 * ((i%19) - 10));
|
||||||
|
|
@ -46,9 +53,14 @@ void test_exact_division()
|
||||||
//std::cout << "n = " << n << std::endl;
|
//std::cout << "n = " << n << std::endl;
|
||||||
assert( exact_division(nd, n) == d);
|
assert( exact_division(nd, n) == d);
|
||||||
assert( exact_division(nd, d) == n);
|
assert( exact_division(nd, d) == n);
|
||||||
|
assert( divides(nd, n) );
|
||||||
|
assert( divides(nd, d) );
|
||||||
}
|
}
|
||||||
// std::cout << "should loop..." << std::endl;
|
|
||||||
// MPF loop = exact_division(MPF(1), MPF(3));
|
assert( ! divides(MPF(1), MPF(3)) );
|
||||||
|
assert( ! divides(MPF(2), MPF(7)) );
|
||||||
|
// test if we're lucky :)
|
||||||
|
assert( ! divides(MPF(CGAL::default_random.get_double()), MPF(CGAL::default_random.get_double())) );
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_equality(int i)
|
void test_equality(int i)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue