CGAL 4.12中Lazy_exact_nt计算不精确?恳请排查原因
CGAL 4.9.1与4.12中小数精度行为差异的原因分析
测试代码
#include <CGAL/Exact_predicates_exact_constructions_kernel.h> typedef CGAL::Exact_predicates_exact_constructions_kernel K; typedef K::FT dbl; void test4() { // 1. Create the smallest possible double and verify double smallDouble(1.0); while(smallDouble/2.0>0) smallDouble/=2.0; if(smallDouble/2.0==0) cout<<"can't divide smallDouble anymore, as expected"<<endl; // 2. Make it a dbl (K::FT) dbl a(smallDouble); // 3. Let b be even smaller ( smaller than the smallest double ) dbl b(a/2.0); // 4. Show interval and try fit_in_double cout<<"a.approx()="<<a.approx()<<endl; cout<<"b.approx()="<<b.approx()<<endl; double d; if(CGAL::internal::fit_in_double(b,d)) { cout<<"Yes, b fits in double d: "<<d<<endl; } else { cout<<"Before b.exact(): b does not fit back into double (as expected)"<<endl; } // 5. Call exact and try fit_in_double again cout<<"\nCalling exact()"<<endl; b.exact(); cout<<"a.approx()="<<a.approx()<<endl; cout<<"b.approx()="<<b.approx()<<endl; if(CGAL::internal::fit_in_double(b,d)) { cout<<"Yes, after exact() b fits in double d: "<<d<<" - Huh, not as expected!"<<endl; } else { cout<<"NOK after exact()"<<endl; } if(b<a) cout<<"b<a, as expected"<<endl; else cout<<"b >= a, not expected"<<endl; double c(to_double(b)); cout<<"c="<<c<<endl; if(c==b) cout<<"c==b, not as expected"<<endl; }
CGAL 4.9.1输出
can't divide smallDouble anymore, as expected a.approx()=[4.94066e-324;4.94066e-324] b.approx()=[0;4.94066e-324] Before b.exact(): b does not fit back into double (as expected) Calling exact() a.approx()=[4.94066e-324;4.94066e-324] b.approx()=[0;4.94066e-324] NOK after exact() b<a, as expected c=0
CGAL 4.12输出
can't divide smallDouble anymore, as expected a.approx()=[4.94066e-324;4.94066e-324] b.approx()=[0;4.94066e-324] Before b.exact(): b does not fit back into double (as expected) Calling exact() a.approx()=[4.94066e-324;4.94066e-324] b.approx()=[4.94066e-324;4.94066e-324] Yes, after exact() b fits in double d: 4.94066e-324 - Huh, not as expected! b >= a, not expected c=4.94066e-324 c==b, not as expected
核心原因分析
你的测试聚焦于CGAL精确构造内核对小于double最小正非正规数的数值处理能力,版本间的差异主要源于以下几个可能的变更:
1. FT类型的底层实现切换
CGAL的Exact_predicates_exact_constructions_kernel(EPECK)的核心是FT(字段类型),它负责精确数值计算:
- 在CGAL 4.9.1中,EPECK默认使用
CORE::Expr作为FT,该类型对极端小的有理数(如2^-1075,即你的测试中a/2)的精确表示支持更稳定,能正确保留其小于a的特性。 - 到CGAL 4.12,默认
FT可能切换为Gmpq(GMP有理数类型)。虽然Gmpq理论上能表示任意有理数,但在处理极端小的指数时,内部可能存在近似或下溢处理逻辑的bug,导致a/2被错误地等价于a。
2. exact()方法的行为变更
FT对象通常会缓存近似值以提升性能,exact()方法的设计初衷是清除近似缓存,强制后续操作使用精确的底层数值。但在CGAL 4.12中:
- 该方法的实现可能被修改,错误地触发了近似逻辑——将
b的精确值替换为其double近似值(即a的值),导致后续的精确比较、转换操作全部偏离预期。
3. fit_in_double与to_double()的逻辑调整
在4.12版本中,这两个工具函数的判断逻辑可能发生了变化:
- 原本
fit_in_double会严格检查FT的精确值是否能被double精确表示,但新版本可能仅检查近似值是否在double范围内,同时错误地认为近似值等于精确值,因此返回true;to_double()也直接返回近似值,还触发了c == b的错误判断。
验证建议
你可以通过以下方式进一步定位问题:
- 检查CGAL 4.12编译依赖:运行
cmake -L查看CGAL_USE_CORE或CGAL_USE_GMP的状态,确认当前使用的FT类型。 - 显式指定
FT类型:将代码中的内核改为CGAL::Exact_predicates_exact_constructions_kernel_with_core(强制使用CORE::Expr),重新编译测试,看是否恢复4.9.1的行为。 - 查阅CGAL版本日志:查看4.10到4.12版本中关于EPECK或
FT的修改记录,确认是否有相关的功能调整或bug修复。
内容的提问来源于stack exchange,提问作者Geom
相关产品推荐
相关产品推荐

