Search code examples
precisioncgal

CGAL: Point on the line?


I've encountered a strange thing in CGAL. I have a line and a point that is supposed to be on this line. This code

typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel;

int main( ) {
  CGAL::Line_2<Kernel> l(0.2, 1.0, -1.4);

  std::cout << l.has_on(CGAL::Point_2<Kernel>(-3.0, 2.0)) << std::endl;
  std::cout << l.y_at_x(-3.0).exact() << std::endl;

  return 0;
}

Produces output:

0
36028797018963967/18014398509481984

OK, maybe the Exact_predicates_exact_constructions_kernel is not good enough... (WHY?)

I tried to use kernel defined with CGAL::Quotient instead:

typedef CGAL::Quotient<CGAL::MP_Float> NT;
typedef CGAL::Cartesian<NT> Kernel;

int main( ) {
  CGAL::Line_2<Kernel> l(0.2, 1.0, -1.4);

  std::cout << l.has_on(CGAL::Point_2<Kernel>(-3.0, 2.0)) << std::endl;
  std::cout << l.y_at_x(-3.0) << std::endl;

  return 0;
}

And the result is even more mysterious to me:

0
2/1

Am I missing something or is it a bug?


Solution

  • When you construct the line from 0.2, two conversions happen. The string "0.2" is converted to a double by the compiler. Then, this double is converted to the number type of the kernel (an exact rational in this case).

    The problem is that the conversion of 0.2 to double is not exact, since 0.2 is a rational that is not representable by a floating-point value, so, some inexactness is introduced. CGAL cannot do anything about this.

    If you need to represent 0.2 exactly, you need to use something like :

      CGAL::Line_2<Kernel> l(NT(2)/NT(10), 1.0, NT(-14)/NT(10));
    

    Alternatively, scale your problem by a power of ten so that all your coordinates becomes integers.

    It is also possible that some rational number types have the ability to construct directly from a string representing a rational number without any rounding, but I don't think CGAL::Quotient<MP_Float> can.