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>
Move `Context::powi` (and the `FBig::powi` convenience, the `CachedFBig` forwarder, and the `num_traits::Pow<IBig>` impls) from `R: Round` to `R: ErrorBounds`, and wrap the binary-exponentiation squaring chain in a Ziv retry loop. The squaring kernel is factored into a shared `powi_chain` helper that also reports whether every step rounded `Exact` (the result is mathematically exact); `exp_compute` reuses it (with its own precision inflation) for the internal `Bⁿ` powering. Repeated squaring compounds the relative error (it roughly doubles per step), so after `bit_len(n)` squarings the error is bounded by about `2^nlen · ulp`, which the Ziv radius (`ulp_w << (nlen + 1)`) reflects. A negative exponent now computes `(1/base)^|n|` directly, so the sign-dependent overflow/underflow falls out naturally (the old reverse-rounding reciprocal trick is dropped). When the squaring chain is exact (the result is exactly representable, e.g. an integer power that fits), the true error is zero and the radius is reported as zero. This is required under the directed rounding modes — `FBig`'s default is `mode::Zero` — where an exactly-representable result lies on a one-sided rounding boundary that no nonzero radius can fit inside (the loop would retry forever). `powf`'s integer-valued-exponent delegation now reaches a correctly-rounded `powi`. Adds a `powi_rounding_exact_oracle` proptest. Co-Authored-By: Claude <noreply@anthropic.com>
Move `Context::powi` (and the `CBig::powi` convenience and the `CachedCBig` forwarder) from `R: Round` to `R: ErrorBounds`, and wrap the binary-exponentiation squaring chain in the complex Ziv driver (N=2, fallible closure). A negative exponent now computes `(1/z)^|n|` directly, so overflow/underflow propagates from the closure with `?` (no separate inversion step). Repeated squaring compounds the relative error (roughly doubling per step), so the per-part radius is `ulp_w << (nlen + 3)` (the `+3` margin covers complex `sqr`/`mul` being near-correct rather than 0.5-ulp correct). When the whole chain — the initial value and every squaring/ multiplication — rounds `Exact`, the result is the mathematically exact `zⁿ` and the radius is reported as zero. As with `dashu-float`'s `powi`, this is required under the directed rounding modes (`CBig`'s default is `mode::Zero`), where an exactly-representable result lies on a one-sided rounding boundary that no nonzero radius can fit inside. Adds a `powi_self_oracle` proptest (positive and negative exponents). Co-Authored-By: Claude <noreply@anthropic.com>
Compute the `1-z²` under `asin`/`acos`'s inner `sqrt` in the factored form `(1-z)(1+z)` instead of the direct `1 - sqr(z)`. Near `z = ±1` the direct form subtracts a `z²` that is dominated by the `sqr` rounding error from 1 (catastrophic cancellation), so `sqrt(1-z²)` — and thus `asin`/`acos` — lost all accuracy there. The factored form is Sterbenz-exact at `z = ±1`: `1-z`'s significand difference is computed exactly, so the existing `ulp·20` Ziv radius stays sound right up to the singularities (the analysis: with the factored form the per-part error near `z=±1` is `~ulp·√|1-z|`, which vanishes at the singularity). `atan` was already sound near its `z = ±i` singularities (its `1 ± iz` is an exact `mul_i` rotation plus an exact-significand add/sub — no cancellation to factor out), so it is unchanged. Adds a `near_one_strategy` and an `asin_acos_near_one_self_oracle` proptest that straddles `z = 1` (where the old form would fail). Co-Authored-By: Claude <noreply@anthropic.com>
Rewrite `Sum for CBig` (and `Sum<&CBig>`) to exact-accumulate, matching `FBig: Sum`. Complex addition is componentwise, so the real and imaginary parts are split (via `CBig::into_parts`) and summed independently through `FBig`'s exact-accumulating `Sum` — each axis accumulates its `Repr`s losslessly and rounds once at the `Context::max` target. The result is the correctly-rounded complex sum. The previous componentwise fold rounded at every step, so a sum like `1e20 + 1 - 1e20` collapsed to `0` at low precision (the `+1` was lost when added to `1e20`); exact accumulation preserves it (yields `1`). `Product` stays a fold, matching `FBig: Product`. `CachedCBig` inherits the fix (its `Sum` delegates to `CBig`'s). Adds `sum_exact_cancellation` and `sum_exact_accumulates_wide_magnitudes` tests. Co-Authored-By: Claude <noreply@anthropic.com>
Update the compliance tables and FAQ (English + Simplified-Chinese mirrors) and V1-ROADMAP.md to reflect the now-completed Ziv work: - `powi` (real and complex) is correctly rounded via its own Ziv loop, no longer "within 1 ulp" / "delegated near-correct". - `asin`/`acos` stay accurate right up to the singularities `z = ±1` via the factored `1-z²=(1-z)(1+z)`; drop the old "lose accuracy near z=±1/±i" caveat. - `CBig: Sum` is exact-accumulating (the roadmap's "not yet correctly-rounded" note is resolved). No heading text changed, so the mdBook CJK auto-anchor IDs are unchanged. 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.