From 1eacf8812bdde8bf6754c545eafe0ce2f19f44e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernd=20G=C3=A4rtner?= Date: Tue, 19 Sep 2006 12:45:19 +0000 Subject: [PATCH] read Gmpq from floating point value in stream --- Number_types/include/CGAL/Gmpq.h | 153 ++++++++++++++++++++++-- Number_types/test/Number_types/Gmpq.C | 68 ++++++++++- Number_types/test/Number_types/makefile | 4 - 3 files changed, 207 insertions(+), 18 deletions(-) diff --git a/Number_types/include/CGAL/Gmpq.h b/Number_types/include/CGAL/Gmpq.h index 6ed47675845..ec663603a93 100644 --- a/Number_types/include/CGAL/Gmpq.h +++ b/Number_types/include/CGAL/Gmpq.h @@ -289,27 +289,156 @@ operator<<(std::ostream& os, const Gmpq &z) return os; } +// inline +// std::istream& +// operator>>(std::istream& is, Gmpq &z) +// { +// char c; +// Gmpz n, d; +// is >> n; +// is >> c; +// //CGAL_assertion(!is || c == '/'); +// if (c != '/'){ +// is.setstate(std::ios_base::failbit); +// return is; +// } +// is >> d; +// if (!is.fail()) { +// z = Gmpq(n,d); +// } +// return is; +// } + +namespace Gmpq_detail { + inline + bool is_space (const std::istream& is, std::istream::int_type c) + { + std::istream::char_type cc= c; + return + (c == std::istream::traits_type::eof()) || +#ifndef CGAL_CFG_NO_LOCALE + std::isspace(cc, std::locale::classic() ) +#else + std::isspace(cc) +#endif + ; + } + + bool is_eof (const std::istream& is, std::istream::int_type c) + { + return c == std::istream::traits_type::eof(); + } + + bool is_digit (const std::istream& is, std::istream::int_type c) + { + std::istream::char_type cc= c; + return +#ifndef CGAL_CFG_NO_LOCALE + std::isdigit(cc, std::locale::classic() ) +#else + std::isdigit(cc) +#endif + ; + } +} + inline std::istream& operator>>(std::istream& is, Gmpq &z) { - char c; - Gmpz n, d; - is >> n; - is >> c; - //CGAL_assertion(!is || c == '/'); - if (c != '/'){ - is.setstate(std::ios_base::failbit); - return is; - } - is >> d; - if (!is.fail()) { - z = Gmpq(n,d); + // reads rational and floating point literals. + const std::istream::char_type zero = '0'; + std::istream::int_type c; + std::ios::fmtflags old_flags = is.flags(); + + is.unsetf(std::ios::skipws); + gmpz_eat_white_space(is); + + Gmpz n(0); // unsigned number before '/' or '.' + Gmpz d(1); // number after '/', or denominator (fp-case) + bool negative = false; // do we have a leading '-'? + bool digits = false; // for fp-case: are there any digits at all? + + c = is.peek(); + if (c != '.') { + // is there a sign? + if (c == '-') { + is.get(); + negative = true; + gmpz_eat_white_space(is); + c=is.peek(); + } + // read n (could be empty) + while (!Gmpq_detail::is_eof(is, c) && Gmpq_detail::is_digit(is, c)) { + digits = true; + n = n*10 + (c-zero); + is.get(); + c = is.peek(); + } + // are we done? + if (Gmpq_detail::is_eof(is, c) || Gmpq_detail::is_space(is, c)) { + is.flags(old_flags); + if (digits && !is.fail()) + z = negative? Gmpq(-n,1): Gmpq(n,1); + return is; + } + } else + n = 0; + + // now we have read n, we are not done, and c is the next character + // in the stream + if (c == '/' || c == '.') { + is.get(); + if (c == '/') { + // rational case + is >> d; + is.flags(old_flags); + if (!is.fail()) + z = negative? Gmpq(-n,d): Gmpq(n,d); + return is; + } + + // floating point case; read number after '.' (may be empty) + while (true) { + c = is.peek(); + if (Gmpq_detail::is_eof(is, c) || !Gmpq_detail::is_digit(is, c)) + break; + // now we have a digit + is.get(); + digits = true; + d *= 10; + n = n*10 + (c-zero); + } } + // now we have read all digits after '.', and c is the next character; + // read the exponential part (optional) + int e = 0; + if (c == 'e' || c == 'E') { + is.get(); + is >> e; + } + + // now construct the Gmpq + if (!digits) { + // illegal floating-point number + is.setstate(std::ios_base::failbit); + is.flags(old_flags); + return is; + } + + // handle e + if (e > 0) + while (e--) n *= 10; + else + while (e++) d *= 10; + is.flags(old_flags); + if (!is.fail()) + z = (negative ? Gmpq(-n,d) : Gmpq(n,d)); return is; } + inline std::pair to_interval (const Gmpq& z) diff --git a/Number_types/test/Number_types/Gmpq.C b/Number_types/test/Number_types/Gmpq.C index 1ce756be933..b57747eafbb 100644 --- a/Number_types/test/Number_types/Gmpq.C +++ b/Number_types/test/Number_types/Gmpq.C @@ -49,8 +49,74 @@ void test_overflow_to_double() assert(CGAL::to_double(val) == 0.5); } +typedef std::pair Test_pair; +typedef std::vector Test_set; +Test_set make_derived_tests (const Test_pair& pair) { + // generate exponents and minus signs + Test_set result; + std::string input = pair.first; + Gmpq should_be = pair.second; + std::string minus = "-"; + for (std::string l; l != "ff"; l += "f") { + result.push_back(Test_pair(input + l , should_be)); + result.push_back(Test_pair(input + "e-2" +l , should_be/100)); + result.push_back(Test_pair(input + "e2" +l, should_be*100)); + result.push_back(Test_pair(input + "e0" +l , should_be)); + result.push_back(Test_pair(minus + input + "e-2" +l, -should_be/100)); + result.push_back(Test_pair(minus + input + "e2" +l, -should_be*100)); + result.push_back(Test_pair(minus + input + "e0" +l, -should_be)); + } + return result; +} + +void test_input_from_float() +{ + std::cout << "Tests Gmpq input from floats." << std::endl; + + Test_set test_set; + // nonnegative integers + test_set.push_back (Test_pair (std::string ("123"), Gmpq(123,1))); + test_set.push_back (Test_pair (std::string ("0"), Gmpq(0,1))); + + // nonnegative floats, with or without digits before/after . + test_set.push_back (Test_pair (std::string ("0.0"), Gmpq(0,1))); + test_set.push_back (Test_pair (std::string ("0."), Gmpq(0,1))); + test_set.push_back (Test_pair (std::string (".0"), Gmpq(0,1))); + test_set.push_back (Test_pair (std::string ("12.34"), Gmpq(1234,100))); + test_set.push_back (Test_pair (std::string ("0.56"), Gmpq(56,100))); + test_set.push_back (Test_pair (std::string (".78"), Gmpq(78,100))); + test_set.push_back (Test_pair (std::string ("90."), Gmpq(90,1))); + test_set.push_back (Test_pair (std::string ("90.0"), Gmpq(90,1))); + + // exponents and signs are automatically generated in + // make_derived_tests + + // now the actual test + std::cout << " Running " << test_set.size() << " master tests..." + << std::endl; + for (unsigned int i=0; i> q; + assert(!is.fail()); + if (!is.eof()) assert(is.peek()=='f'); + if (q != should_be) + std::cout << " Error: " << derived[j].first + " read as " + << q << std::endl; + assert(q == should_be); + } + } +} + int main() { + // Added by Bernd Gaertner + test_input_from_float(); + // Added by Daniel Russel std::cout << "Testing IO" << std::endl; { @@ -160,9 +226,7 @@ int main() { test_overflow_to_double(); test_overflow_to_interval(Gmpz()); test_overflow_to_interval(Gmpq()); - - #ifdef CGAL_USE_GMPXX test_overflow_to_interval(mpz_class()); test_overflow_to_interval(mpq_class()); diff --git a/Number_types/test/Number_types/makefile b/Number_types/test/Number_types/makefile index fed99400cef..8e1cc003ee0 100644 --- a/Number_types/test/Number_types/makefile +++ b/Number_types/test/Number_types/makefile @@ -16,8 +16,6 @@ include $(CGAL_MAKEFILE) CXXFLAGS = \ -Iinclude \ -I../../include \ - $(TESTSUITE_CXXFLAGS) \ - $(EXTRA_FLAGS) \ $(CGAL_CXXFLAGS) \ $(LONG_NAME_PROBLEM_CXXFLAGS) @@ -26,11 +24,9 @@ CXXFLAGS = \ #---------------------------------------------------------------------# LIBPATH = \ - $(TESTSUITE_LIBPATH) \ $(CGAL_LIBPATH) LDFLAGS = \ - $(TESTSUITE_LDFLAGS) \ $(LONG_NAME_PROBLEM_LDFLAGS) \ $(CGAL_LDFLAGS)