Skip to content

Commit 8a01680

Browse files
committed
implement two more prescaled tiny trig backends
1 parent 4968de6 commit 8a01680

1 file changed

Lines changed: 68 additions & 0 deletions

File tree

src/computable/node.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,6 +1031,21 @@ impl Computable {
10311031
}
10321032
}
10331033

1034+
fn prescaled_asin(value: Computable) -> Computable {
1035+
// Tiny non-rational asin inputs use the direct odd-power series. This
1036+
// mirrors prescaled atan/asinh dispatch and avoids building the generic
1037+
// atan/sqrt transform once the argument is structurally small.
1038+
crate::trace_dispatch!("computable", "constructor", "prescaled-asin");
1039+
let sign = value.exact_sign();
1040+
Self {
1041+
internal: Box::new(Approximation::PrescaledAsin(value)),
1042+
cache: RefCell::new(Cache::Invalid),
1043+
bound: RefCell::new(BoundCache::Invalid),
1044+
exact_sign: RefCell::new(sign.map_or(ExactSignCache::Invalid, ExactSignCache::Valid)),
1045+
signal: None,
1046+
}
1047+
}
1048+
10341049
fn asinh_rational_deferred(rational: Rational) -> Computable {
10351050
// Same series as `prescaled_asinh`, but exact rationals can skip the
10361051
// child Computable wrapper and feed the kernel directly.
@@ -1045,6 +1060,21 @@ impl Computable {
10451060
}
10461061
}
10471062

1063+
fn prescaled_atanh(value: Computable) -> Computable {
1064+
// Tiny non-rational atanh inputs use the direct odd-power series. This
1065+
// keeps parity with exact-rational AtanhRational and avoids the heavier
1066+
// log-ratio graph for already-small symbolic arguments.
1067+
crate::trace_dispatch!("computable", "constructor", "prescaled-atanh");
1068+
let sign = value.exact_sign();
1069+
Self {
1070+
internal: Box::new(Approximation::PrescaledAtanh(value)),
1071+
cache: RefCell::new(Cache::Invalid),
1072+
bound: RefCell::new(BoundCache::Invalid),
1073+
exact_sign: RefCell::new(sign.map_or(ExactSignCache::Invalid, ExactSignCache::Valid)),
1074+
signal: None,
1075+
}
1076+
}
1077+
10481078
fn atanh_rational_deferred(rational: Rational) -> Computable {
10491079
// Tiny exact-rational atanh uses the odd series directly. Keeping the
10501080
// Rational payload avoids rebuilding a Ratio node in cold approximation
@@ -3343,6 +3373,11 @@ impl Computable {
33433373
crate::trace_dispatch!("computable", "asin", "known-negative-symmetry");
33443374
return self.negate().asin().negate();
33453375
}
3376+
let (_, planned_msd) = self.planning_sign_and_msd();
3377+
if planned_msd.flatten().is_some_and(|msd| msd <= -4) {
3378+
crate::trace_dispatch!("computable", "asin", "structural-tiny-prescaled");
3379+
return Self::prescaled_asin(self);
3380+
}
33463381

33473382
crate::trace_dispatch!("computable", "asin", "generic-atan-sqrt-transform");
33483383
Self::asin_deferred(self)
@@ -3595,6 +3630,11 @@ impl Computable {
35953630
crate::trace_dispatch!("computable", "atanh", "known-negative-symmetry");
35963631
return self.negate().atanh().negate();
35973632
}
3633+
let (_, planned_msd) = self.planning_sign_and_msd();
3634+
if planned_msd.flatten().is_some_and(|msd| msd <= -4) {
3635+
crate::trace_dispatch!("computable", "atanh", "structural-tiny-prescaled");
3636+
return Self::prescaled_atanh(self);
3637+
}
35983638

35993639
// General formula 1/2 * ln((1+x)/(1-x)). Tiny exact rationals avoid
36003640
// this path because the odd atanh series has much less setup.
@@ -5436,6 +5476,34 @@ mod tests {
54365476
assert_approx(near_one.atanh(), -40, "7976218668587", 2);
54375477
}
54385478

5479+
#[test]
5480+
fn tiny_non_rational_asin_uses_prescaled_series() {
5481+
let tiny = Computable::rational(Rational::new(2))
5482+
.sqrt()
5483+
.shift_left(-20);
5484+
let result = tiny.clone().asin();
5485+
5486+
assert!(matches!(
5487+
result.internal.as_ref(),
5488+
Approximation::PrescaledAsin(_)
5489+
));
5490+
assert_close(result, Computable::asin_deferred(tiny), -80, 4);
5491+
}
5492+
5493+
#[test]
5494+
fn tiny_non_rational_atanh_uses_prescaled_series() {
5495+
let tiny = Computable::rational(Rational::new(2))
5496+
.sqrt()
5497+
.shift_left(-20);
5498+
let result = tiny.clone().atanh();
5499+
5500+
assert!(matches!(
5501+
result.internal.as_ref(),
5502+
Approximation::PrescaledAtanh(_)
5503+
));
5504+
assert_close(result, Computable::atanh_direct_deferred(tiny), -80, 4);
5505+
}
5506+
54395507
#[test]
54405508
fn inverse_hyperbolic_computable_kernels_approximate_expected_values() {
54415509
let half = Computable::rational(Rational::fraction(1, 2).unwrap());

0 commit comments

Comments
 (0)