Skip to content

Commit 56d312f

Browse files
Number_types: fix Lazy_exact_nt operator<< to write exact() value
Change operator<< for Lazy_exact_nt to output a.exact() instead of to_double(a). The old to_double() conversion loses precision and breaks round-trip save/load of exact kernel coordinates (issue #135). The corresponding operator>> already uses read_float_or_quotient() which handles both floating-point and rational (n/d) formats, so no changes are needed on the input side. Add Lazy_exact_nt_io.cpp regression test verifying round-trip I/O for rationals, integers, negative values, computed sums, large values, and zero.
1 parent 9ec3716 commit 56d312f

4 files changed

Lines changed: 110 additions & 2 deletions

File tree

Number_types/doc/Number_types/CGAL/Lazy_exact_nt.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,9 @@ static double get_relative_precision_of_to_double();
111111
}; /* end Lazy_exact_nt */
112112

113113
/*!
114-
writes `m` to ostream `out` in an interval format.
114+
writes the exact value `m.exact()` to ostream `out`.
115+
This enables round-trip save/load of `Lazy_exact_nt` values without
116+
loss of precision: `operator>>` reconstructs the same exact number.
115117
\relates Lazy_exact_nt
116118
*/
117119
std::ostream& operator<<(std::ostream& out, const Lazy_exact_nt<NT>& m);

Number_types/include/CGAL/Lazy_exact_nt.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1303,7 +1303,12 @@ const Lazy_exact_nt<ET> & y){
13031303
template <typename ET>
13041304
std::ostream &
13051305
operator<< (std::ostream & os, const Lazy_exact_nt<ET> & a)
1306-
{ return os << CGAL_NTS to_double(a); }
1306+
{
1307+
// Output the exact value so that operator>> can reconstruct the same number.
1308+
// Using to_double() (as was done before issue #135 was fixed) loses precision
1309+
// and breaks round-trip save/load of exact kernel coordinates.
1310+
return os << a.exact();
1311+
}
13071312

13081313
template <typename ET>
13091314
std::istream &

Number_types/test/Number_types/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ create_single_source_cgal_program("Interval_nt_new.cpp")
3030
create_single_source_cgal_program("ioformat.cpp")
3131
create_single_source_cgal_program("known_bit_size_integers.cpp")
3232
create_single_source_cgal_program("Lazy_exact_nt.cpp")
33+
create_single_source_cgal_program("Lazy_exact_nt_io.cpp")
3334
create_single_source_cgal_program("Lazy_exact_nt_new.cpp")
3435
create_single_source_cgal_program("leda_bigfloat.cpp")
3536
create_single_source_cgal_program("leda_bigfloat_interval.cpp")
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// Regression test for issue #135:
2+
// Lazy_exact_nt operator<< used to_double(), losing precision and breaking
3+
// round-trip save/load of exact kernel coordinates.
4+
5+
#include <CGAL/Lazy_exact_nt.h>
6+
#include <CGAL/Gmpq.h>
7+
8+
#include <cassert>
9+
#include <iostream>
10+
#include <sstream>
11+
12+
typedef CGAL::Lazy_exact_nt<CGAL::Gmpq> Lazy_nt;
13+
14+
int main()
15+
{
16+
// Test 1: exact rational round-trip
17+
{
18+
std::cout << "Test 1: round-trip of 1/3" << std::endl;
19+
Lazy_nt a(CGAL::Gmpq(1, 3));
20+
std::ostringstream oss;
21+
oss << a;
22+
// Should output "1/3", not "0.333333..."
23+
std::istringstream iss(oss.str());
24+
Lazy_nt b;
25+
iss >> b;
26+
assert(a == b);
27+
std::cout << " OK: wrote \"" << oss.str() << "\", read back equal" << std::endl;
28+
}
29+
30+
// Test 2: integer value
31+
{
32+
std::cout << "Test 2: round-trip of 42" << std::endl;
33+
Lazy_nt a(42);
34+
std::ostringstream oss;
35+
oss << a;
36+
std::istringstream iss(oss.str());
37+
Lazy_nt b;
38+
iss >> b;
39+
assert(a == b);
40+
std::cout << " OK: wrote \"" << oss.str() << "\"" << std::endl;
41+
}
42+
43+
// Test 3: negative rational
44+
{
45+
std::cout << "Test 3: round-trip of -5/7" << std::endl;
46+
Lazy_nt a(CGAL::Gmpq(-5, 7));
47+
std::ostringstream oss;
48+
oss << a;
49+
std::istringstream iss(oss.str());
50+
Lazy_nt b;
51+
iss >> b;
52+
assert(a == b);
53+
std::cout << " OK: wrote \"" << oss.str() << "\"" << std::endl;
54+
}
55+
56+
// Test 4: value that cannot be represented exactly as double
57+
// 1/3 + 1/7 = 10/21, to_double() would give 0.476190476190...
58+
{
59+
std::cout << "Test 4: round-trip of computed 1/3 + 1/7 = 10/21" << std::endl;
60+
Lazy_nt a(CGAL::Gmpq(1, 3));
61+
Lazy_nt b(CGAL::Gmpq(1, 7));
62+
Lazy_nt c = a + b;
63+
std::ostringstream oss;
64+
oss << c;
65+
std::istringstream iss(oss.str());
66+
Lazy_nt d;
67+
iss >> d;
68+
assert(c == d);
69+
std::cout << " OK: wrote \"" << oss.str() << "\"" << std::endl;
70+
}
71+
72+
// Test 5: very large rational that would lose precision as double
73+
{
74+
std::cout << "Test 5: round-trip of large rational" << std::endl;
75+
Lazy_nt a(CGAL::Gmpq(CGAL::Gmpz("99999999999999999999"), CGAL::Gmpz("100000000000000000007")));
76+
std::ostringstream oss;
77+
oss << a;
78+
std::istringstream iss(oss.str());
79+
Lazy_nt b;
80+
iss >> b;
81+
assert(a == b);
82+
std::cout << " OK: wrote \"" << oss.str() << "\"" << std::endl;
83+
}
84+
85+
// Test 6: zero
86+
{
87+
std::cout << "Test 6: round-trip of 0" << std::endl;
88+
Lazy_nt a(0);
89+
std::ostringstream oss;
90+
oss << a;
91+
std::istringstream iss(oss.str());
92+
Lazy_nt b;
93+
iss >> b;
94+
assert(a == b);
95+
std::cout << " OK: wrote \"" << oss.str() << "\"" << std::endl;
96+
}
97+
98+
std::cout << "All tests passed." << std::endl;
99+
return 0;
100+
}

0 commit comments

Comments
 (0)