forked from 1b-agent/fieldcraft-upstream
mesh: exact predicates (Shewchuk adaptive, symbolic perturbation, indirect LPI/TPI) (#8)
* mesh: exact predicates (Shewchuk adaptive orient/incircle/insphere), symbolic perturbation, indirect LPI/TPI predicates Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * mesh: predicate benchmark (filter hit rate, CAD-degenerate inputs); record the predicate decision Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * mesh: simplify predicates (destructuring over fallbacks, shared-minor exact insphere, cheaper expansion product) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm --------- Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
17 files changed
+2683
-2
No files matched your search
@@ -0,0 +1,96 @@
|
||||
//! Error-free transformations (Shewchuk 1997, §2.3 and §2.5).
|
||||
//!
|
||||
//! Each function returns `(x, y)` with `x = fl(a ∘ b)` and `y` the exact roundoff, so that
|
||||
//! `a ∘ b = x + y` exactly. They assume IEEE 754 binary64 with round-to-nearest-even, no
|
||||
//! intermediate extended precision and no fused multiply-add contraction; that is what Rust's
|
||||
//! `f64` guarantees on every target ModelCraft builds for (x86_64 with SSE2, aarch64, wasm32):
|
||||
//! rustc never contracts `a * b + c` into an FMA and does not use x87. No overflow or underflow
|
||||
//! may occur, which the coordinate domain of [`super::Point2`] / [`super::Point3`] guarantees.
|
||||
|
||||
/// `2^ceil(53 / 2) + 1`, the splitter for Dekker's product (§2.5, Theorem 17).
|
||||
const SPLITTER: f64 = 134_217_729.0;
|
||||
|
||||
/// Fast-Two-Sum (Theorem 6). Requires `|a| >= |b|` (or `a == 0`).
|
||||
#[inline(always)]
|
||||
pub(crate) fn fast_two_sum(a: f64, b: f64) -> (f64, f64) {
|
||||
let x = a + b;
|
||||
let b_virtual = x - a;
|
||||
(x, b - b_virtual)
|
||||
}
|
||||
|
||||
/// Two-Sum (Theorem 7), no precondition on the magnitudes.
|
||||
#[inline(always)]
|
||||
pub(crate) fn two_sum(a: f64, b: f64) -> (f64, f64) {
|
||||
let x = a + b;
|
||||
let b_virtual = x - a;
|
||||
let a_virtual = x - b_virtual;
|
||||
let b_round = b - b_virtual;
|
||||
let a_round = a - a_virtual;
|
||||
(x, a_round + b_round)
|
||||
}
|
||||
|
||||
/// The roundoff of `x = fl(a - b)`, given `x`.
|
||||
#[inline(always)]
|
||||
pub(crate) fn two_diff_tail(a: f64, b: f64, x: f64) -> f64 {
|
||||
let b_virtual = a - x;
|
||||
let a_virtual = x + b_virtual;
|
||||
let b_round = b_virtual - b;
|
||||
let a_round = a - a_virtual;
|
||||
a_round + b_round
|
||||
}
|
||||
|
||||
/// Two-Diff: `a - b = x + y` exactly.
|
||||
#[inline(always)]
|
||||
pub(crate) fn two_diff(a: f64, b: f64) -> (f64, f64) {
|
||||
let x = a - b;
|
||||
(x, two_diff_tail(a, b, x))
|
||||
}
|
||||
|
||||
/// Dekker's split (Theorem 17): `a = hi + lo` with each half fitting in 26 bits.
|
||||
#[inline(always)]
|
||||
pub(crate) fn split(a: f64) -> (f64, f64) {
|
||||
let c = SPLITTER * a;
|
||||
let a_big = c - a;
|
||||
let hi = c - a_big;
|
||||
(hi, a - hi)
|
||||
}
|
||||
|
||||
/// Two-Product with `b` already split (Theorem 18).
|
||||
#[inline(always)]
|
||||
pub(crate) fn two_product_presplit(a: f64, b: f64, b_hi: f64, b_lo: f64) -> (f64, f64) {
|
||||
let x = a * b;
|
||||
let (a_hi, a_lo) = split(a);
|
||||
let err1 = x - a_hi * b_hi;
|
||||
let err2 = err1 - a_lo * b_hi;
|
||||
let err3 = err2 - a_hi * b_lo;
|
||||
(x, a_lo * b_lo - err3)
|
||||
}
|
||||
|
||||
/// Two-Product (Theorem 18): `a * b = x + y` exactly.
|
||||
#[inline(always)]
|
||||
pub(crate) fn two_product(a: f64, b: f64) -> (f64, f64) {
|
||||
let (b_hi, b_lo) = split(b);
|
||||
two_product_presplit(a, b, b_hi, b_lo)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn transforms_are_exact_on_classic_cases() {
|
||||
let (x, y) = two_sum(1.0, 1e-17);
|
||||
assert_eq!((x, y), (1.0, 1e-17));
|
||||
// 2^53 + 1 is a tie that rounds to the even 2^53.
|
||||
let (x, y) = fast_two_sum(2f64.powi(53), 1.0);
|
||||
assert_eq!((x, y), (2f64.powi(53), 1.0));
|
||||
let (x, y) = two_diff(1.0, 1e-17);
|
||||
assert_eq!((x, y), (1.0, -1e-17));
|
||||
// (1 + 2^-30)^2 = 1 + 2^-29 + 2^-60: the last term is the roundoff.
|
||||
let a = 1.0 + 2f64.powi(-30);
|
||||
let (x, y) = two_product(a, a);
|
||||
assert_eq!((x, y), (1.0 + 2f64.powi(-29), 2f64.powi(-60)));
|
||||
let (hi, lo) = split(a);
|
||||
assert_eq!(hi + lo, a);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user