Conversation
Migrate the remaining real transcendentals to guaranteed-correct rounding via the Ziv retry loop (`Context::ziv`/`ziv_pair`): - Trig (sin, cos, sin_cos, tan, asin, acos, atan, atan2): extract near-correct `_compute` series cores and wrap them in Ziv. The argument reduction folds a `|k|·ulp(π/2)` reduction-error term into the radius so the containment test stays sound for huge `|x|`. - Hyperbolic (sinh, cosh, sinh_cosh, tanh, asinh, acosh, atanh): composition-based, treating the Ziv-correct `exp_m1`/`ln_1p` as black boxes; the unreachable exp-overflow case is hoisted via a shared `exp_overflows` probe (closures can't return `Err`). - `hypot`: composition-based (sqrt attenuates), small-constant radius. `sin_cos`/`sinh_cosh` certify both halves via the new `ziv_pair` driver. `powf` is left near-correct — its `exp(y·ln x)` amplification makes the data-dependent radius converge poorly for large results, so a dedicated treatment is deferred. Bound propagation: the migrated `Context`/`FBig`/`CachedFBig` methods move to `R: ErrorBounds`; dashu-cmplx's complex `sqrt`/`norm`/`abs`/ `arg` follow (they route through `hypot`/`atan2`). Tests: 20 exact-oracle soundness tests (`f(x)@p == f(x)@2p re-rounded`) covering every migrated function; existing trig/hyper/exp suites pass. Docs: float CHANGELOG, V1-ROADMAP, and bilingual compliance/faq updated. Co-Authored-By: Claude <noreply@anthropic.com>
…to powi
powf with a non-integer exponent is now correctly rounded via the Ziv loop.
The fix for the previously-deferred amplification problem: the error radius is
result.ulp()*(|y*ln x|+1)*(B+8) taken at the *working* precision, so it shrinks
as B^{-guard} and the containment test converges. A radius computed at unlimited
precision is constant across retries and never settles for a value near a
rounding boundary, which is why the earlier attempt infinite-retried.
An integer-valued exponent now delegates to powi (binary exponentiation), gated
on Repr::is_int. This also admits a negative base (sign fixed by the exponent's
parity) -- the old OutOfDomain TODO. That path is near-correct (<=1 ulp),
matching powi. The exp overflow case is hoisted out of the Ziv closure.
Also: Repr::is_int is now const (pure exponent/sentinel check).
Co-Authored-By: Claude <noreply@anthropic.com>
acos(1) = pi/2 - asin(1) cancels to exactly 0, but the composition carries a positive radius. Under a directed rounding mode (Down/Zero/Up) the preimage of 0 is one-sided ([0, ulp)), so the Ziv containment test can never certify it -- any positive radius dips the interval below 0 -- and it infinite-retried. This was the signed_zero.rs full-file hang: test_asin_acos_unit_under_down hung on acos(1) under Down. Short-circuit x = 1 to the exact 0 before the Ziv loop, matching asin's existing |x| = 1 special case. (asin(+-1) = +-pi/2 and acos(-1) = pi are nonzero, so they were already fine.) Co-Authored-By: Claude <noreply@anthropic.com>
A scan of every transcendental at its exact-representable special inputs under Down/Up/Zero turned up two more instances of the acos(1) hang (a transcendental whose true value is exactly representable carries a positive radius that can't be certified against the value's one-sided preimage, so Ziv infinite-retries): - asin(0) = 0: asin lacked the x=0 short-circuit that asinh/atanh/sin/cos/... all have, so it computed atan(0)=0 with a positive radius and infinite-retried. Now short-circuits to +-0 (asin is odd), matching the rest of the family. - acos(-1) = pi: short-circuited for symmetry with acos(1). The scan confirmed no other transcendentals hang (sqrt of perfect squares, exp(0), cos(0), cosh(0), powf(x,0), nth_root of exact roots, etc. are all already special-cased or detect exact results). Remaining: hypot of an exact Pythagorean triple (e.g. hypot(3,4)=5) -- same containment class but no clean single special case, plus a dashu-int NTT crash hit during the retry; tracked separately. Co-Authored-By: Claude <noreply@anthropic.com>
add_signed_sqr_conv and add_signed_mul_conv assumed a non-zero input (debug_assert!(la_bits > 0)); an all-zero slice panicked in debug and indexed out of bounds in release. sqrt_rem of a perfect square with many trailing zero words squares the estimate's all-zero low half to verify the remainder, so this was reachable -- and was the crash behind dashu-float's hypot(3,4) under directed rounding (the Ziv retry drove sqrt to a precision where the scaled significand hit the NTT path). An all-zero operand now returns early: the product is zero, so c += sign*a*b (or a^2) is a no-op. This matches the existing zero-guard in the chunked-multiply closure. Regression test: sqrt_rem of (1<<560000)^2. Co-Authored-By: Claude <noreply@anthropic.com>
The float changelog's hypot note said the underlying dashu-int NTT crash was "tracked separately"; that crash is now fixed (a482d7e), so update the note to reflect that hypot(exact) still infinite-retries on the containment issue, but no longer crashes. Co-Authored-By: Claude <noreply@anthropic.com>
Replace the ratio form (large * sqrt(1 + (small/large)^2)) with the scaled direct form sqrt(large^2 + small^2), tracking each step's Exact/Inexact flag (MPFR's `exact` flag in hypot.c). When the whole chain is exact -- which holds for every Pythagorean-triple input -- the closure reports radius 0, which ziv accepts without the containment test. This is the only way to terminate an exactly-representable result under directed rounding, where the value sits on a one-sided preimage boundary. The ratio form broke this for triples like hypot(5,12)=13: 5/12 is a non-terminating binary fraction, so div returned Inexact even though the final result 13 is exact. The direct form has no division, so sqr/add/sqrt are all exact for integer inputs -- mirroring how MPFR avoids a division precisely so its exact flag works for every triple. Both operands are scaled down by k base-B digits before squaring (k chosen so large^2 can't overflow the exponent) and the root scaled back, exactly MPFR's sh. Regression tests: hypot of (3,4)/(5,12)/(8,15) under Down/Up/Zero all return the exact integer. Co-Authored-By: Claude <noreply@anthropic.com>
- tan: drop the hoisted pole check. It re-evaluated the sin/cos series a second time on top of the first Ziv attempt (~2x per call), and guarded a condition that can't occur: near a pole the value is large but finite, and dashu's wide exponent range holds it (sign carried by the arithmetic, s/-|c| is negative); the reduced argument cancels to exactly zero only when the input equals the work-precision pi/2, which the trig guard inflation rules out. The closure's significand-zero retry guard handles that unreachable case. Matches MPFR's structure (no pole special-case). - Dedup helpers: Context::base_guard_digits (ceil(log_B(precision))) across the 12 transcendental Ziv guard sites; series_radius (ulp*(4*terms+12)) across the sin/cos/sin_cos/atan/ln series cores; merge the two textually identical CachedFBig forwarding macros. Co-Authored-By: Claude <noreply@anthropic.com>
Centralize the unlimited-precision check in Context::guard (the guard-digit recipe is an inherently limited-precision technique, so guard rejects precision 0 rather than silently making a finite 0+GUARD context). All transcendentals that build a guard context via guard -- exp, log, abs, sqrt, and complex sin/cos/tan/sin_cos -- now panic on an unlimited context as documented (previously they silently computed at the fixed guard precision and marked the truncated value Exact). asin/acos/atan and powf build their work context directly (Context::new(p+GUARD), bypassing guard) and so assert explicitly. The exact special-value shortcuts still bypass the check. Arithmetic switches from guard to a new Context::work_context that uses the exact self.float() (precision 0) at unlimited: mul/sqr/norm are now exact there (were guard-rounded, a latent bug); div/inv panic via dashu-float's div (a quotient isn't exactly representable). The check is kept private to dashu-cmplx (a pub(crate) helper) rather than re-exported from dashu-float. Co-Authored-By: Claude <noreply@anthropic.com>
Wrap dashu-cmplx's complex transcendentals (exp, log, powf, sin/cos/tan/ sin_cos, asin/acos/atan, sqrt) in a Ziv retry loop that certifies *both* the real and imaginary parts, matching dashu-float's real transcendentals. The driver lives in a new complex/src/ziv.rs, reimplemented in dashu-cmplx rather than reusing dashu-float's pub(crate) ziv/ziv_pair (invisible across crates). Two complex-specific choices vs float's driver: - const-generic part count N (2 for one complex result, 4 for sin_cos, which shares one sin_cos/sinh_cosh evaluation across sin and cos); - a *fallible* closure |guard| -> Result<[((v,e)); N], FpError>, so overflow/ domain propagate via ? with no hoisted per-function probe and no closure unwraps. The per-part contained test runs on FBig at FloatCtxt::new(0) (public-API exact arithmetic). Each transcendental reports a provable per-part radius (result.ulp() * C, plus an amplification term where composition magnifies error: log's ln|z| near |z|=1, tan's real part for large |Im z|, powf's result magnitude). abs delegates straight to the (already correctly-rounded) real hypot, dropping a double-rounding re-round. The dead Context::guard() is removed; the driver asserts limited precision, keeping the panic-at-unlimited contract. Known limitation: tan for large |Im z| and asin/acos/atan right at their singularities lose accuracy (formula cancellation); the well-conditioned regime is guaranteed-correctly rounded. tan's cancellation is fixed by the double-angle formula in a follow-up. Self-oracle tests tightened 2-4 -> 1 ULP; added tan/powf/abs oracles, a retry-count test, and an overflow-propagation test. Co-Authored-By: Claude <noreply@anthropic.com>
Replace tan's sin(z)/cos(z) (computed through the complex div, which
catastrophically cancels in the real part for large |Im z| — sin and cos
are both ~cosh(y)-scale, so sin·conj(cos) cancels from ~cosh²y down to
O(1)) with the double-angle identity
tan(x+iy) = (sin 2x + i sinh 2y) / (cos 2x + cosh 2y).
The denominator cos 2x + cosh 2y is a sum of a bounded term and one >= 1,
so it never cancels; the result is accurate for all finite |Im z|, and a
plain result.ulp() * C radius is sound (the B^{1-pw} workaround and the
large-|Im z| caveat are both gone). The only small-denominator points are
the real-axis poles (y = 0, x = pi/2 + k pi), where the large value is
genuine.
tan now operates on the float parts (FloatCtxt) directly, like exp/log/
sqrt, rather than driving whole-CBig sin_cos + div. Broadened the
tan_self_oracle to a moderate-large |Im z| strategy (up to ~40) and added
a tan_large_imaginary_is_near_i test. Docs/changelog updated to drop tan
from the limitation list.
Co-Authored-By: Claude <noreply@anthropic.com>
The LAST_ZIV_RETRIES thread_local! (used by the retry-count tests) needs std — thread_local! isn't in scope under --no-default-features, so both float/src/ziv.rs and complex/src/ziv.rs failed to compile in the no_std CI job (cargo test --no-default-features --features rand). Gate the counter, its loop uses, and the counter-reading tests behind feature = "std" (#[cfg(all(test, feature = "std"))]). The Ziv driver itself is now no_std-clean; the retry-count tests still run under std as before. Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.