You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:42:14