From 4c6bdc90c4860e6c156cbc2695b60ac7bf81b154 Mon Sep 17 00:00:00 2001 From: Jacob Zhong Date: Tue, 30 Jun 2026 14:19:09 +0800 Subject: [PATCH 01/21] Add mdBook guide infra: KaTeX preprocessor + CI build-check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - guide/book.toml: add [output.html] (git-repo + edit links) and [preprocessor.katex]; drop `multilingual` (removed in mdBook 0.5). - .github/workflows/guide.yml: build-check job that installs mdBook 0.5.3 and mdbook-katex (0.10.0-alpha from upstream git, pinned commit) then runs `mdbook build guide`. Build-check only — no deploy this phase. The released mdbook-katex crate (0.9.x, built on mdBook 0.4) is incompatible with mdBook 0.5's preprocessor protocol, hence the pinned git alpha. mdbook-katex 0.10.0-alpha renders math server-side and auto-injects the KaTeX CSS, so no theme/CSS config is needed. Co-Authored-By: Claude --- .github/workflows/guide.yml | 37 +++++++++++++++++++++++++++++++++++++ guide/book.toml | 7 ++++++- 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/guide.yml diff --git a/.github/workflows/guide.yml b/.github/workflows/guide.yml new file mode 100644 index 00000000..23509d84 --- /dev/null +++ b/.github/workflows/guide.yml @@ -0,0 +1,37 @@ +on: + push: + branches: + - master + - develop + pull_request: + branches: + - master + - develop + +name: Guide + +# Build-check for the mdBook user guide under `guide/`. Renders the book with mdBook and the +# mdbook-katex preprocessor (KaTeX math typesetting). The build fails on errors and — because +# mdBook errors on any `SUMMARY.md` entry whose target file is missing — also guards against +# broken internal links. This phase is build-check only: nothing is deployed. +# +# Version note: mdBook's 0.5.x line changed the preprocessor wire protocol, so the *released* +# mdbook-katex crate (0.9.x, built against mdbook 0.4) cannot talk to mdBook 0.5. We therefore +# install mdBook's 0.5 release and mdbook-katex's mdbook-0.5 build from upstream git, both pinned +# for reproducibility. Bump both together when upgrading. + +jobs: + guide: + name: Build guide (mdBook + KaTeX) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + - name: Install mdBook + run: cargo install mdbook --locked --version 0.5.3 + - name: Install mdbook-katex (mdbook 0.5 build) + run: cargo install --locked --git https://github.com/lzanini/mdbook-katex --rev 1f63ab8605cea8e975be4b001dd7e39dd5367834 + - name: Build guide + run: mdbook build guide diff --git a/guide/book.toml b/guide/book.toml index 2b6171e8..848a8791 100644 --- a/guide/book.toml +++ b/guide/book.toml @@ -3,5 +3,10 @@ title = "Dashu user guide" description = "User guide of the dashu arbitrary precision library" authors = ["Jacob Zhong"] language = "en" -multilingual = false src = "src" + +[output.html] +git-repository-url = "https://github.com/cmpute/dashu" +edit-url-template = "https://github.com/cmpute/dashu/edit/master/guide/src/{path}" + +[preprocessor.katex] From 6ee05c029d1e0d48a9ea1a9078d7285e29296c0c Mon Sep 17 00:00:00 2001 From: Jacob Zhong Date: Tue, 30 Jun 2026 14:28:57 +0800 Subject: [PATCH 02/21] =?UTF-8?q?Guide=20Batch=201:=20foundation=20pages?= =?UTF-8?q?=20=E2=80=94=20types/convert/construct?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - types.md: add CBig as a numeric type (two-Repr-over-shared-Context model, C99 Annex G no-NaN model, ZERO/ONE/I constants). - convert.md: fill the FBig/DBig conversion sections (precision/base/rounding, integers & primitive floats, RBig interop) and add a CBig conversions section. - construct.md: add CBig constants/from_parts/cbig! macro, and fill the CachedFBig gaps (extra constructors, direct ConstCache API, !Send/!Sync note, worked cache-reuse example). Math now uses KaTeX ($...$) per the mdbook-katex setup. Co-Authored-By: Claude --- guide/src/construct.md | 74 +++++++++++++++++++++++++++++++++++++++++- guide/src/convert.md | 64 +++++++++++++++++++++++++++++++++--- guide/src/types.md | 11 +++++++ 3 files changed, 144 insertions(+), 5 deletions(-) diff --git a/guide/src/construct.md b/guide/src/construct.md index bc964976..f2c6b09d 100644 --- a/guide/src/construct.md +++ b/guide/src/construct.md @@ -8,6 +8,7 @@ For all the numeric types, there are several constants associated with the type. - `IBig`: `::ZERO`, `::ONE`, `::NEG_ONE` - `FBig`/`DBig`: `::ZERO`, `::ONE`, `::NEG_ONE`, `::INFINITY`, `::NEG_INFINITY` - `RBig`: `::ZERO`, `::ONE`, `::NEG_ONE` +- `CBig`: `::ZERO` ($0+0i$), `::ONE` ($1+0i$), `::I` ($0+1i$) # Raw Constructor for `UBig` @@ -25,6 +26,7 @@ The components of different types are listed below: - `IBig` = sign: `Sign` + magnitude: `UBig` - `FBig`/`DBig` = significand: `IBig` + exponent: `isize` - `RBig` = numerator: `IBig` + denominator: `UBig` + - `CBig` = real part: `FBig` + imaginary part: `FBig` (the result precision is the larger of the two) - For `::from_parts_const()` - `IBig` = sign: `Sign` + magnitude: `DoubleWord` - `FBig`/`DBig` = sign: `Sign` + significand: `DoubleWord` + exponent: `isize` @@ -36,7 +38,7 @@ To deconstruct these numeric types, use the `::into_parts()` functions to get th # `dashu-macros` -We also provide a convenient and efficient way to create large numbers from literals through the macros `ubig!`/`ibig!`/`fbig!`/`dbig!`/`rbig`. These macros can be obtained directly from the `dashu-macros` crate or from the `dashu` meta crate. +We also provide a convenient and efficient way to create large numbers from literals through the macros `ubig!`/`ibig!`/`fbig!`/`dbig!`/`rbig!`/`cbig!`. These macros can be obtained directly from the `dashu-macros` crate or from the `dashu` meta crate. The `cbig!` macro accepts the same algebraic form as `CBig`'s `FromStr` (e.g. `cbig!(3+4i)`, `cbig!(-i)`) or a `re, im` pair (e.g. `cbig!(3, 4)`). You can directly put numeric literals as the argument without quotes (e.g. `dbig!(3.1415926535897932384626)`), and you don't need to worry about precision loss, because it's guaranteed that the number is faithfully created without approximations. Besides, the macros have minimal runtime overhead, since the numbers are preprocessed by the macros during compile-time. @@ -122,3 +124,73 @@ transcendental operation will recompute constants from scratch: cached.clear_cache(); assert_eq!(cached.cache().total_terms(), 0); ``` + +## More constructors and accessors + +Beyond `into_cached` / `with_cache` / `From`, `CachedFBig` mirrors the rest of `FBig`'s construction surface while preserving the cache handle: + +- `from_parts(significand, exponent)` — build from a significand and exponent, with a fresh cache. +- `with_rounding::()` — change the rounding mode, keeping the cache handle. +- `as_fbig()` — borrow the inner `FBig` immutably (cheap; no cache detach). +- `from_repr(repr, context, cache)` / `into_repr()` — the raw-repr constructor/destructor that share a specific cache handle. + +## Computing constants directly + +The cache stores exact binary-splitting state for the constants π, ln2, and ln10, so the methods that produce them reuse and progressively extend prior work rather than recomputing from scratch. On `CachedFBig`, π is a single call: + +```rust +use std::rc::Rc; +use core::cell::RefCell; +use dashu_float::{CachedFBig, ConstCache}; +use dashu_float::round::mode::HalfAway; + +let cache = Rc::new(RefCell::new(ConstCache::new())); +let _pi = CachedFBig::::pi(100, &cache); +// a later, higher-precision call extends the same cached state instead of restarting +let _pi_more = CachedFBig::::pi(1000, &cache); +``` + +You can also drive a bare `ConstCache` directly, without a `CachedFBig` — useful when you want the constants but not the per-value wrapper. The methods are generic over base and rounding mode, and a single cache serves any base: + +```rust +use dashu_float::ConstCache; +use dashu_float::round::mode::HalfAway; + +let mut cache = ConstCache::new(); +let pi = cache.pi::<10, HalfAway>(100).value(); // computes from scratch +let pi_1000 = cache.pi::<10, HalfAway>(1000).value(); // extends the cached state +let ln2 = cache.ln2::<10, HalfAway>(100); +let ln10 = cache.ln10::<10, HalfAway>(100); +``` + +`ln_base::(precision)` dispatches to the cached ln2 / ln10 when `B` is 2 or 10 (or a power of two), and falls back to a direct `ln(B)` otherwise. + +## Thread safety + +`CachedFBig` carries its cache as `Rc>`, so it is **`!Send + !Sync`** — a cached value cannot move across threads. `FBig` itself stays `Copy + Send + Sync` (which is why `static_fbig!` keeps working); only the cached wrapper is non-thread-safe. `ConstCache` is a plain struct of big integers and is itself `Send + Sync`, so to share one cache across threads, wrap a `ConstCache` (or a `CachedFBig`) in `Arc>`. The underlying `Context` methods accept `Option<&mut ConstCache>` regardless of the container, so this needs no API change. + +## Worked example: reusing constants across a chain + +Because every value-producing operation preserves the cache handle, a chain of transcendentals reuses the same constants throughout. Building several results from one shared handle pays for each constant once: + +```rust +use std::rc::Rc; +use core::cell::RefCell; +use dashu_float::{CachedFBig, ConstCache, Context, Repr}; +use dashu_float::round::mode::HalfAway; + +type F = CachedFBig; +let cache = Rc::new(RefCell::new(ConstCache::new())); + +// π is computed into the shared cache... +let _pi_50 = F::pi(50, &cache); +// ...and a later, higher-precision call extends it instead of restarting +let _pi_1000 = F::pi(1000, &cache); + +// an arithmetic chain built on the same handle keeps it end to end +let a = F::from_repr(Repr::new(2.into(), 0), Context::new(50), cache.clone()); +let b = F::from_repr(Repr::new(3.into(), 0), Context::new(50), cache.clone()); +let _ = (a + b).ln().exp(); + +assert!(cache.borrow().total_terms() > 0); +``` diff --git a/guide/src/convert.md b/guide/src/convert.md index 976296aa..7d9fc86f 100644 --- a/guide/src/convert.md +++ b/guide/src/convert.md @@ -66,16 +66,72 @@ The conversions from and to primitive numbers are also implemented for the `dash ## Conversion for FBig/DBig +Conversions involving `FBig`/`DBig` are richer than for the integer types, because a floating-point number carries three independent knobs: a **base**, a **precision** (a cap on the number of significant digits), and a **rounding mode**. Most conversions therefore come in two flavors — an infallible `From`/`Into` when no information is lost, and a fallible `TryFrom`/`TryInto` when exactness is required. ## Conversion to different base / precision / rounding mode -TODO: `with_rounding`, `with_precision`, `with_base`, `to_binary`, `to_decimal`, etc. -(how precision is determined) +The base, precision, and rounding mode are changed independently: + +- `with_rounding::()` reinterprets the same value under a different rounding mode — the underlying representation is unchanged, only the context's rounding field moves, so no rounding occurs. +- `with_precision(p)` widens or shrinks the significand to `p` digits. Widening is always exact (`Approximation::Exact`); shrinking rounds per `R` and returns `Approximation::Inexact` carrying the rounding direction. + +```rust +use dashu_base::Approximation::*; +use dashu_float::DBig; +use dashu_float::round::Rounding::*; + +let a = DBig::from_str("2.345")?; +assert_eq!(a.precision(), 4); +assert_eq!(a.clone().with_precision(3), Inexact(DBig::from_str("2.35")?, AddOne)); +assert_eq!(a.clone().with_precision(5), Exact(DBig::from_str("2.345")?)); +``` + +- `with_base::()` converts to a different base. The result precision is chosen so the significand cap is no larger than before — the largest integer $p'$ with $\mathrm{NewB}^{\,p'} \le B^{\,p}$. Conversion is exact when one base is a power of the other; otherwise it rounds per `R`. `with_base_and_precision::(p)` lets you set the target precision explicitly. + +For the common binary ↔ decimal hops, two shortcuts pick the rounding mode for you: `to_decimal()` is `with_rounding::().with_base::<10>()` (yielding a `DBig`), and `to_binary()` is `with_rounding::().with_base::<2>()`. + +> These methods panic if the associated context has **unlimited precision** and the conversion cannot be done losslessly — set a precision first. ## Conversion to integers or primitive floats -TODO: convert from UBig/IBig to FBig, the precision will be inferred. convert from FBig to UBig/IBig +Converting *into* `FBig` from `UBig`/`IBig` (or any primitive integer) infers the precision from the magnitude: the result precision equals the number of significant base-`B` digits of the integer. + +Going the other way, `TryFrom for IBig`/`UBig` succeeds only when the float is finite and exactly integer-valued — `ConversionError::OutOfBounds` for infinities, `LossOfPrecision` for a fractional part. For a rounding-aware path use `to_int()`, which always succeeds and reports the rounding direction: + +```rust +use dashu_base::Approximation::*; +use dashu_float::DBig; +use dashu_float::round::Rounding::*; + +assert_eq!(DBig::from_str("1234")?.to_int(), Exact(1234.into())); +assert_eq!(DBig::from_str("1.234")?.to_int(), Inexact(1.into(), NoOp)); +``` + +To a primitive float, `to_f32()` / `to_f64()` return `Rounded` / `Rounded` carrying the rounding direction; they never fail (overflow yields `±∞`, infinities map to infinities). The reverse — `TryFrom`/`TryFrom for FBig` — is **base-2 only** (it is almost always lossy in any other base); to reach a non-binary `FBig`, convert to base 2 first and then call `with_base()`. NaN is rejected with `ConversionError::OutOfBounds`. ## Conversion to RBig -TODO: `simplest_in()`, `simplest_from_*()`, `.nearest_in()`, `next_up()`, `next_down()`, etc. +With the optional `dashu-float` feature enabled on `dashu-ratio`, `TryFrom for RBig` succeeds only when the float is exactly rational-representable, and `RBig::to_float()` is the rounding-aware path in the other direction. + +For approximating a float by a *simple* rational (the smallest numerator/denominator within a tolerance), use `simplest_from_f32` / `simplest_from_f64`, or the interval queries `simplest_in`, `nearest_in`, `next_up`, and `next_down` on `FBig`/`DBig` — these treat the float's own rounding interval as the search bound. + +## Conversion for CBig + +A `CBig` is reached losslessly from any real value: `From`, `From`, and `From` embed the value as the real part with imaginary `+0` (exact, unlimited precision). The inverse is fallible — `TryFrom for FBig` extracts the real part only when the imaginary part is zero (both `±0` count), and `TryFrom for IBig` further requires the real part to be integer-valued. Both compose the `CBig → FBig → IBig` chain, mirroring `FBig`'s own `From`/`TryFrom` split. + +```rust +use dashu_cmplx::CBig; +use dashu_float::{FBig, round::mode::HalfAway}; + +type C = CBig; +type F = FBig; + +// a real value embeds as a purely-real complex number +let z = C::from(F::from(7)); +assert_eq!(z.re().significand(), &7.into()); +assert!(z.im().is_zero()); + +// extracting the real part fails when the imaginary part is nonzero +let w = C::from_parts(F::from(3), F::from(4)); +assert!(F::try_from(w).is_err()); +``` diff --git a/guide/src/types.md b/guide/src/types.md index 5df430e5..3ff656e4 100644 --- a/guide/src/types.md +++ b/guide/src/types.md @@ -7,6 +7,7 @@ In `dashu` crates, there are standalone types for each kind of numbers with arbi - `dashu_float::FBig` (alias `dashu::Real`) represents real numbers with floating point representation (`signficand * base ^ exponent`) - `dashu_float::DBig` (alias `dashu::Decimal`) is a specialization of `FBig` with `base = 10`. - `dashu_ratio::RBig` (alias `dashu::Rational`) represents rational numbers. It has a variant `dashu_ratio::Relaxed`, which also represents a rational number, but it doesn't enforce that the number is in the canonicalized form. +- `dashu_cmplx::CBig` (alias `dashu::Complex`) represents complex numbers, built as a pair of `FBig` parts sharing one precision and rounding mode. Common operations are implemented for all these numeric types, please refer to the other sections or the API docs for the usages. @@ -32,6 +33,16 @@ The most fundamental type of the `dashu` libraries is the natural number `UBig`. The layout of `FBig` (and `DBig`) is a little different from other types. An `FBig` instance contains a number representation `dashu_float::Repr` and a context `dashu_float::Context`. The context will be copied every time a new `FBig` is created based on it. The context currently contains the rounding information and the precision associated with this number. The context is kept deliberately lightweight (`Copy` + `Send` + `Sync`): the shared cache for math constants (such as π, ln2, ln10) lives *outside* the context, in the separate [`CachedFBig`](./construct.md#cached-arithmetic-for-fbig) wrapper, so that a plain `FBig` stays cheap to copy and usable in `const`/`static` contexts. Therefore, if you don't want to store the additional context information, you can just store the `Repr` part of the `FBig`. The later operations on the `Repr` can be called with the associated methods of the `Context`, which all takes the reference to a `Repr` instance. However, this could lead to a little overhead in some cases. +## Complex Numbers + +The `CBig` type (in the `dashu-cmplx` crate) is an arbitrary-precision complex number, and it is generic over a rounding mode `R` and a base `B`, just like `FBig`. A `CBig` instance holds two `Repr` parts — the real part `re` and the imaginary part `im` — over a **single shared** `Context`. + +Storing one context — rather than wrapping two independent `FBig`s (each carrying its own) — makes the uniform-precision invariant *physical*: there is exactly one precision slot, so `re` and `im` structurally cannot disagree. Each part keeps its own significand length; the shared context holds only the precision cap and the single rounding mode, which is applied independently to each component. The result context of `CBig::from_parts(re, im)` is the larger of the two (an unlimited `0` precision is treated as the minimum, so a limited operand always wins), and widening the smaller part to it is exact. + +Because both parts are `Repr`, `CBig` inherits `dashu-float`'s signed-zero / signed-infinity / branch-cut machinery directly. Rounding follows the C99 Annex G / Kahan model that `dashu-float` already implements for reals — see [Standards Compliance](./compliance.md) for the full special-value and branch-cut tables. As with `FBig`, there is **no NaN**: C99 cases that would produce a complex NaN are reported as `FpError` at the context layer (and panic at the convenience layer). + +`CBig` exposes the constants `CBig::ZERO` ($0+0i$), `CBig::ONE` ($1+0i$), and `CBig::I` ($0+1i$, the imaginary unit), all at unlimited precision. There is no `CBig::INFINITY` constant: the complex infinity is the single Riemann point $+\infty+i\cdot0$, produced by `proj` and by overflow. Construction, arithmetic, and the rest of the surface are covered in [Construction and Destruction](./construct.md), [Conversion](./convert.md), and the [Operations](./ops/index.md) pages. + # Auxiliary Types Besides the numeric types defined in separate crates, there are some auxiliary types defined in the crate **dashu-base**. From 04f84a1917e9775bb1732c525e1363724c4b37ab Mon Sep 17 00:00:00 2001 From: Jacob Zhong Date: Tue, 30 Jun 2026 14:36:57 +0800 Subject: [PATCH 03/21] =?UTF-8?q?Guide=20Batch=202:=20I/O=20pages=20?= =?UTF-8?q?=E2=80=94=20parse/print/serialize/interop/index?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - index.md: hub linking the I/O topics. - print.md: integer (Display/Binary/Octal/Hex/in_radix), float (Display and scientific e/E/@ markers), rational (num/den + in_expanded repetend), and complex (algebraic a+bi) formatting; the existing Debug head..tail section is kept verbatim. - parse.md: FromStr / from_str_radix for each type, incl. the float exponent forms and the complex algebraic grammar. - serialize.md: bytes / serde / rkyv. - interop.md: to_digits/from_digits, byte access, word access. Co-Authored-By: Claude --- guide/src/io/index.md | 8 +++ guide/src/io/interop.md | 33 ++++++++++++- guide/src/io/parse.md | 62 +++++++++++++++++++++-- guide/src/io/print.md | 100 +++++++++++++++++++++++++++++++------- guide/src/io/serialize.md | 25 +++++++--- 5 files changed, 199 insertions(+), 29 deletions(-) diff --git a/guide/src/io/index.md b/guide/src/io/index.md index e69de29b..cde2ff1e 100644 --- a/guide/src/io/index.md +++ b/guide/src/io/index.md @@ -0,0 +1,8 @@ +# Input and Output + +dashu's numeric types participate in Rust's standard formatting and parsing traits, plus a few dashu-specific APIs for radix conversion, positional expansion, and byte-level serialization. This section covers: + +- [Parsing](./parse.md) — `FromStr` and `from_str_radix` for every type, including the float exponent forms. +- [Printing](./print.md) — `Display`, `Debug`, the `Binary`/`Octal`/`LowerHex`/`UpperHex` traits, `in_radix`, and the rational positional expansion. +- [Serialization](./serialize.md) — byte sequences, `serde`, and `rkyv`. +- [Interoperability](./interop.md) — low-level digit / byte / word access to a `UBig`'s raw representation. diff --git a/guide/src/io/interop.md b/guide/src/io/interop.md index 99a877fd..874a78f2 100644 --- a/guide/src/io/interop.md +++ b/guide/src/io/interop.md @@ -1 +1,32 @@ -Document `from_words`, `as_words`, `to_le_bytes/to_be_bytes` (and `from_*`), `to_digits` \ No newline at end of file +# Interoperability + +Besides the standard formatting and parsing traits, `dashu-int` exposes lower-level access to a `UBig`'s raw representation, for interoperating with other libraries or building custom (de)serialization. + +## Digit access + +`UBig::to_digits(base)` returns the number's digits in any base `2..=Word::MAX` (most-significant first, stored as `Word`), and `UBig::from_digits(base, &digits)` reconstructs it. This generalizes `in_radix` (which is limited to base 2–36 for string output) to arbitrary bases and word-sized digits. + +```rust +use dashu_int::UBig; + +let n = UBig::from(0x1234u16); +let digits = n.to_digits(16); // [1, 2, 3, 4], most-significant first +assert_eq!(UBig::from_digits(16, &digits)?, n); +``` + +## Byte access + +`to_le_bytes` / `to_be_bytes` and `from_le_bytes` / `from_be_bytes` give a portable, explicit-endianness byte representation — see [Serialization](./serialize.md). + +## Word access + +`UBig::from_words(&[w0, w1, …])` builds a value from little-endian words, and `.as_words()` borrows the underlying word slice without copying. This is the closest to the raw in-memory form. + +```rust +use dashu_int::{UBig, Word}; + +let n = UBig::from_words(&[3, 2, 1]); // 3 + 2·Word + 1·Word² +let words: &[Word] = n.as_words(); +``` + +> The exact in-memory layout of a `UBig` is not yet stabilized — don't rely on the word layout across versions. diff --git a/guide/src/io/parse.md b/guide/src/io/parse.md index e30e2e82..fc1af083 100644 --- a/guide/src/io/parse.md +++ b/guide/src/io/parse.md @@ -1,5 +1,61 @@ -# Standard Parsing API +# Parsing -`from_str`, `from_str_radix` +Every numeric type implements `FromStr`, so values can be built with `"...".parse()?` or `T::from_str(...)`. Underscore separators are allowed in all numeric literals. -# Float number parsing +## Parsing Integers + +`UBig::from_str` / `IBig::from_str` accept an optional sign followed by decimal digits. For other bases use `from_str_radix(s, radix)` (radix 2–36); it recognizes a `0x`/`0o`/`0b` prefix independently of the `radix` argument. + +```rust +use dashu_int::{UBig, IBig}; +use core::str::FromStr; + +assert_eq!(UBig::from_str("12345")?, UBig::from(12345u16)); +assert_eq!(IBig::from_str_radix("-1aff", 16)?, IBig::from(-0x1aff)); +``` + +## Parsing Floats + +`FBig`/`DBig` `FromStr` reads the significand in the value's native base, with the exponent in one of these forms: + +| Form | Meaning | Base | +|------|---------|------| +| `aaa` / `aaa.` / `aaa.bbb` | fixed point | any | +| `aaa.bbb@cc` | significand × base^cc | any | +| `aaa.bbbEcc` / `aaa.bbbecc` | significand × 10^cc | 10 | +| `0xaaa.bbbPcc` | hex significand × 2^cc | 2 | + +Precision is inferred from the number of significant digits presented. String `inf`/`NaN` literals are **not** accepted — construct infinities from the `INFINITY` constant instead. + +```rust +use dashu_float::DBig; +use core::str::FromStr; + +assert_eq!(format!("{:e}", DBig::from_str("6.022e23")?), "6.022e23"); +assert_eq!(DBig::from_str("-0.0123456789")?.to_string(), "-0.0123456789"); +``` + +## Parsing Rationals + +`RBig::from_str` accepts `numerator/denominator`, or just a numerator (denominator defaults to 1). `from_str_radix` parses both parts in the given base; a `0x`/`0o`/`0b` prefix must be consistent between them. + +```rust +use dashu_ratio::RBig; +use core::str::FromStr; + +assert_eq!(RBig::from_str("22/7")?.to_string(), "22/7"); +``` + +## Parsing Complex + +`CBig` `FromStr` accepts the same algebraic $a+bi$ grammar that `Display` emits: an optional real term plus an optional signed imaginary term (at least one required); a unit coefficient may be omitted (`i`, `-i`). The MPC-style parenthesized form `(re im)` is **not** accepted. + +```rust +use dashu_cmplx::CBig; +use dashu_float::round::mode::HalfAway; +use core::str::FromStr; + +type C = CBig; +assert_eq!(C::from_str("1+2i")?.to_string(), "1+2i"); +assert_eq!(C::from_str("-i")?.to_string(), "-i"); +``` diff --git a/guide/src/io/print.md b/guide/src/io/print.md index ba9c962a..04526bbf 100644 --- a/guide/src/io/print.md +++ b/guide/src/io/print.md @@ -1,19 +1,26 @@ -# Standard Format API +# Printing -`UBig` and `IBig` support the full set of Rust standard formatter traits: -[`Display`], [`Debug`], [`Binary`], [`Octal`], [`LowerHex`], [`UpperHex`]. -All of them support the sign, width, fill, padding, and alignment options of -[`Formatter`]. For custom radices use [`InRadix`] (see below). +`UBig` and `IBig` support the full set of Rust standard formatter traits: `Display`, `Debug`, `Binary`, `Octal`, `LowerHex`, `UpperHex`. The float, rational, and complex types support `Display` and `Debug`, with extra radix/positional helpers described below. All of them honor the sign, width, fill, padding, and alignment options of `Formatter`. -TODO: describe the `in_radix` API +## Integer Formatting + +`Display` renders a `UBig`/`IBig` in decimal. The `Binary`, `Octal`, `LowerHex`, and `UpperHex` traits render in base 2/8/16, with the `#` flag adding the conventional `0b`/`0o`/`0x`/`0X` prefix. For any other radix, use `in_radix(r)` (base 2–36); its `#` flag uppercases digits above 9. + +```rust +use dashu_int::UBig; + +let n = UBig::from(255u8); +assert_eq!(format!("{}", n), "255"); +assert_eq!(format!("{:#x}", n), "0xff"); +assert_eq!(format!("{:#b}", n), "0b11111111"); + +assert_eq!(format!("{}", n.in_radix(16)), "ff"); +assert_eq!(format!("{:#}", n.in_radix(16)), "FF"); +``` ## Debug Print -The [`Debug`] implementation uses a compact **head‥tail** format for large -integers: it prints the most significant digits, a `..` separator, and the -least significant digits, omitting the middle. For small integers that fit in -a single [`Word`] or [`DoubleWord`] the full number is shown without -truncation. +The `Debug` implementation uses a compact **head‥tail** format for large integers: it prints the most significant digits, a `..` separator, and the least significant digits, omitting the middle. For small integers that fit in a single `Word` or `DoubleWord` the full number is shown without truncation. There are two forms, controlled by the formatter flags: @@ -39,14 +46,11 @@ assert_eq!( ); ``` -The number of digits shown on each end depends on the [`Word`] size — -on 64-bit targets it is 19 decimal digits at each end (one word's worth), -on 32-bit targets it is 9 digits. +The number of digits shown on each end depends on the `Word` size — on 64-bit targets it is 19 decimal digits at each end (one word's worth), on 32-bit targets it is 9 digits. ### Verbose form (`{:#?}`) -Adds `(digits: N, bits: M)` after the head‥tail representation, showing the -total digit count and bit length. +Adds `(digits: N, bits: M)` after the head‥tail representation, showing the total digit count and bit length. ```rust use dashu_int::{UBig, Word}; @@ -60,6 +64,66 @@ if Word::BITS == 64 { } ``` -## Rational Number Formatting +## Float Formatting + +`FBig`/`DBig` `Display` renders the significand with the radix point positioned by the exponent — the natural positional form, not scientific. The formatter precision option rounds to that many fractional digits. + +```rust +use dashu_float::DBig; +use core::str::FromStr; + +assert_eq!(format!("{}", DBig::from_str("12.34")?), "12.34"); +assert_eq!(format!("{:.1}", DBig::from_str("12.34")?), "12.3"); +``` + +For scientific notation use `LowerExp`/`UpperExp`: the exponent marker is `e`/`E` in base 10 and `@` in other bases. `Debug` prints `significand * base ^ exponent (prec: N)` (or a struct with `{:#?}`). Infinities render as `inf` / `-inf` under both `Display` and `Debug`. + +```rust +use dashu_float::DBig; +use core::str::FromStr; + +assert_eq!(format!("{:e}", DBig::from_str("1234.5")?), "1.2345e3"); +assert_eq!(format!("{:E}", DBig::from_str("1234.5")?), "1.2345E3"); +``` + +## Rational Formatting + +`RBig`/`Relaxed` `Display` renders as `numerator/denominator`, or just the numerator when the denominator is `1`. The `Binary`/`Octal`/`LowerHex`/`UpperHex` traits and `in_radix(r)` format both parts in the given base. + +```rust +use dashu_ratio::RBig; +use core::str::FromStr; + +assert_eq!(format!("{}", RBig::from_str("22/7")?), "22/7"); +assert_eq!(format!("{}", RBig::from_str("5/1")?), "5"); +``` + +For the positional (decimal) expansion use `in_expanded()`. `{:.N}` prints exactly `N` fractional digits; the `#` flag detects the repeating part and parenthesizes it: + +```rust +use dashu_ratio::RBig; + +let x = RBig::from_parts(1.into(), 3u8.into()); +assert_eq!(format!("{:.4}", x.in_expanded()), "0.3333"); +assert_eq!(format!("{:#}", x.in_expanded()), "0.(3)"); +``` + +## Complex Formatting + +`CBig` `Display` uses the algebraic $a+bi$ notation: the imaginary term always carries an explicit sign, a unit coefficient is elided (`i`, not `1i`), and a zero imaginary part is omitted. `Debug` prints `re: im: (prec:

)`. + +```rust +use dashu_cmplx::CBig; +use dashu_float::{FBig, round::mode::HalfAway}; + +type C = CBig; +type F = FBig; + +assert_eq!(format!("{}", C::from_parts(F::from(1), F::from(2))), "1+2i"); +assert_eq!(format!("{}", C::from_parts(F::from(-3), F::from(-4))), "-3-4i"); +assert_eq!(format!("{}", C::from_parts(F::from(5), F::from(0))), "5"); +assert_eq!(format!("{}", C::from_parts(F::from(0), F::from(1))), "i"); +assert_eq!(format!("{}", C::from_parts(F::from(0), F::from(-1))), "-i"); +``` -TODO: rational numbers have both in_radix and in_expanded functions, other than normal traits +The same algebraic grammar is accepted on input — see [Parsing](./parse.md). diff --git a/guide/src/io/serialize.md b/guide/src/io/serialize.md index 120eb2fa..3e1ea141 100644 --- a/guide/src/io/serialize.md +++ b/guide/src/io/serialize.md @@ -1,16 +1,27 @@ +# Serialization + ```text -The layout for serialized numbers is protected by the semver. The change of the layout is considered as a break change and a new major version will be published. +The layout for serialized numbers is protected by semver. A change to the layout is considered a breaking change and a new major version will be published. ``` -# Conversion to Bytes +dashu offers three layers of (de)serialization for its integer and float types, chosen by how portable or fast the format must be. + +## Conversion to Bytes -(use `to_le_bytes`, `to_be_bytes`, `from_le_bytes`, `from_be_bytes`) +`UBig` and `IBig` convert to and from explicit-endianness byte sequences via `to_le_bytes` / `to_be_bytes` and `from_le_bytes` / `from_be_bytes`. These are portable, layout-stable formats suitable for binary interchange. -# Serialization with `serde` +```rust +use dashu_int::UBig; + +let n = UBig::from(0x12345678u32); +let bytes = n.to_le_bytes(); +assert_eq!(UBig::from_le_bytes(&bytes), n); +``` -(Use serde for best platform compatibility and memory efficiency. Note that we support the `is_human_readable` option.) +## Serialization with `serde` -# Serialization with `rkyv` +With the `serde` feature enabled, every numeric type implements `Serialize` / `Deserialize`. The human-readable form (when `is_human_readable()` is true) is a string, for easy use with JSON/TOML; the compact binary form is used otherwise. Only the binary form's layout is semver-protected. -(Use rkyv for best speed.) +## Serialization with `rkyv` +With the `rkyv` feature enabled, zero-copy (de)serialization is available for the integer types — fastest for same-architecture scenarios, at the cost of a less portable layout. From a08aded102781b8006b7e3d0dcfab299321b3939 Mon Sep 17 00:00:00 2001 From: Jacob Zhong Date: Tue, 30 Jun 2026 14:47:17 +0800 Subject: [PATCH 04/21] Guide Batch 3: Operations pages (7, incl. new trig_n_hyper) - index.md: operations hub. - basic.md: arithmetic per type (integer/float/rational/complex) + mixed-type note. - cmp.md: equality, ordering, sign, AbsOrd, NumOrd/NumHash. - bit.md: bitwise ops, BitTest (bit/bit_len), set/clear/trailing_zeros, shifts, UBig as a bit vector. - exp_log.md: two-layer API; real exp/log/powers/roots/constants; complex exp/ln/sqrt/pow with the exp/log identities and the branch cut. - trig_n_hyper.md (NEW): real + complex circular trig and real hyperbolic; complex hyperbolics noted as deferred to 0.5.x. - num_theory.md: Gcd/ExtendedGcd, ConstDivisor/Reduced modular arithmetic. - SUMMARY.md: add the Trigonometric and Hyperbolic Functions entry. Co-Authored-By: Claude --- guide/src/SUMMARY.md | 1 + guide/src/ops/basic.md | 42 ++++++++++++++++++++++++++++++++++- guide/src/ops/bit.md | 40 +++++++++++++++++++++++++++++---- guide/src/ops/cmp.md | 19 +++++++++++----- guide/src/ops/exp_log.md | 28 ++++++++++++++++++++++- guide/src/ops/index.md | 10 +++++++++ guide/src/ops/num_theory.md | 34 +++++++++++++++++++++++++--- guide/src/ops/trig_n_hyper.md | 18 +++++++++++++++ 8 files changed, 177 insertions(+), 15 deletions(-) create mode 100644 guide/src/ops/trig_n_hyper.md diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md index c68216a7..6dcdd30e 100644 --- a/guide/src/SUMMARY.md +++ b/guide/src/SUMMARY.md @@ -14,6 +14,7 @@ - [Equality and Comparison](./ops/cmp.md) - [Basic Arithmetics](./ops/basic.md) - [Exponential and Logarithm](./ops/exp_log.md) + - [Trigonometric and Hyperbolic Functions](./ops/trig_n_hyper.md) - [Bit Manipulation](./ops/bit.md) - [Number Theoretic](./ops/num_theory.md) - [FAQ](./faq.md) diff --git a/guide/src/ops/basic.md b/guide/src/ops/basic.md index eeda561a..ed96fc6b 100644 --- a/guide/src/ops/basic.md +++ b/guide/src/ops/basic.md @@ -1 +1,41 @@ -(+-*) (clarify behavior of division and modulo on different types) \ No newline at end of file +# Basic Arithmetics + +The standard arithmetic operators are implemented for all numeric types, for both owned and borrowed operands. The behavior of division and remainder differs by type. + +## Integer Arithmetic + +`UBig` and `IBig` support `+`, `-`, `*`, `/`, and `%`. Integer division rounds toward zero, and the remainder takes the sign of the dividend (the C/Rust convention). For Euclidean division (non-negative remainder) use the `DivRemEuclid` / `RemEuclid` traits from `dashu-base`; `DivRem` returns both quotient and remainder at once. + +```rust +use dashu_int::IBig; + +let b = IBig::from(-0x10ff); +let e = 2 * &b - 1; // mixes naturally with primitives +assert_eq!(e, IBig::from(-0x21ff)); +``` + +## Float Arithmetic + +`FBig`/`DBig` support `+`, `-`, `*`, `/` between values of the **same base and rounding mode** (mixed bases are a compile error by design). The result precision is `max(lhs.precision, rhs.precision)`, and each operation reports its inexactness through the two-layer API described in [Exponential and Logarithm](./exp_log.md). Infinities are terminal: `1/0` and `ln(0)` produce `±∞`, but feeding an infinity back into arithmetic is an error (`FpError::InfiniteInput`). + +## Rational Arithmetic + +`RBig` supports `+`, `-`, `*`, `/`. Division by zero panics. `Relaxed` performs the same operations without auto-reducing to lowest terms (faster for a chain of operations); call `canonicalize()` to reduce when needed. + +## Complex Arithmetic + +`CBig` supports the field operations `+`, `-`, `*`, `/`, plus `sqr` and `inv` (multiplicative inverse). Multiplication and division by a real `FBig` are also available as mixed-type operators. Multiplication and division use Smith's method with a guard digit and re-round, giving the same near-correctly-rounded guarantee as `dashu-float`'s transcendentals. + +```rust +use dashu_cmplx::CBig; +use dashu_float::{FBig, round::mode::HalfAway}; + +type C = CBig; +let z = C::from_parts(FBig::from(3), FBig::from(4)); +let sum = &z + &C::I; // (3+4i) + i = 3+5i +assert_eq!(sum.im().significand(), &5.into()); +``` + +## Mixed-type arithmetic + +There are **no implicit mixed-type operators** between different big-number kinds (e.g. `UBig + FBig` does not compile) — convert explicitly first (see [Conversion](../convert.md)). diff --git a/guide/src/ops/bit.md b/guide/src/ops/bit.md index a9670d49..e9a89088 100644 --- a/guide/src/ops/bit.md +++ b/guide/src/ops/bit.md @@ -1,4 +1,36 @@ -(clarify that it follows the two's complement rule) -## Bit Operators -## Use `UBig` as a Bit Vector -(clear bits, set bit, bit len, chunk bits) \ No newline at end of file +# Bit Manipulation + +`UBig` and `IBig` support the bitwise operators `&` (and), `|` (or), `^` (xor), and `!` (not). On `UBig`, `!` is an *infinite-width* complement — every bit above the highest set bit is treated as `1`, so `!n` is generally a very large number. On `IBig`, `!` follows the two's-complement rule. + +```rust +use dashu_int::UBig; + +let a = UBig::from(0b1100u8); +let b = UBig::from(0b1010u8); +assert_eq!(format!("{:b}", &a & &b), "1000"); +assert_eq!(format!("{:b}", &a | &b), "1110"); +``` + +## Bit testing and length + +The `BitTest` trait (from `dashu-base`) tests and measures individual bits: `.bit(n)` returns the `n`-th bit, and `.bit_len()` returns the position of the highest set bit plus one. `set_bit(n)` / `clear_bit(n)` mutate a `UBig` in place, and `trailing_zeros()` counts the low-order zero bits. + +## Shifts + +`<<` and `>>` shift by a `usize`. Left shifts grow the number; right shifts shrink it and are equivalent to floor-division by a power of two. + +## Using `UBig` as a bit vector + +Because a `UBig` has unbounded width, it works naturally as an arbitrarily large bit set: set bit `i` with `set_bit(i)`, test it with `bit(i)`, and read the extent with `bit_len()`. + +```rust +use dashu_base::BitTest; +use dashu_int::UBig; + +let mut bits = UBig::ZERO; +bits.set_bit(0); +bits.set_bit(100); +assert!(bits.bit(0) && bits.bit(100)); +assert!(!bits.bit(1)); +assert_eq!(bits.bit_len(), 101); +``` diff --git a/guide/src/ops/cmp.md b/guide/src/ops/cmp.md index 8528d3c7..170ad7c5 100644 --- a/guide/src/ops/cmp.md +++ b/guide/src/ops/cmp.md @@ -1,12 +1,19 @@ -(Comparison is natively enabled only between big numbers, but not for native types due to [`num-bigint`#150](https://github.com/rust-num/num-bigint/issues/150)). To compare with native types, use `NumOrd`) +# Equality and Comparison -# Ordering +Comparison is natively enabled **only between big numbers of the same kind**, not between big numbers and primitive types — this avoids the trait-overlap problem described in [`num-bigint`#150](https://github.com/rust-num/num-bigint/issues/150). To compare a big number with a primitive type, enable the `num-order` feature and use the `NumOrd` trait. -## Comparison -## Sign ## Equality -# Hashing +`PartialEq`/`Eq` is value equality. For `FBig`/`DBig` it compares the representation and ignores the context (precision and rounding mode), so two floats with different precision but the same value compare equal. Signed zeros compare equal: `+0 == -0`. `CBig` compares componentwise, with `+0 == -0` on each part. + +## Ordering + +`UBig`/`IBig`/`RBig`/`FBig`/`DBig` carry the natural numeric total order (`Ord`). Infinities are placed at the ends: $-\infty < \text{finite} < +\infty$. `CBig` defines a lexicographic total order by `(re, then im)` — usable for sorting and `BTreeMap`, but note it is *not* an algebraic magnitude ordering. + +## Sign + +The signed types (`IBig`, `FBig`/`DBig`, `RBig`, `CBig`) expose `.sign()` (returning `dashu_base::Sign`, where zero is `Positive`) and `.signum()` (returning `-1`, `0`, or `+1` as the same type). -(NumHash) +## Magnitude comparison and cross-type ordering +`AbsOrd` (from `dashu-base`) compares by absolute value; for `CBig` it compares by $|z|$. The `num-order` feature adds `NumOrd` for ordering and `NumHash` for hashing across different numeric types (big and primitive), keeping them consistent with each other. diff --git a/guide/src/ops/exp_log.md b/guide/src/ops/exp_log.md index d5742dc8..96469c12 100644 --- a/guide/src/ops/exp_log.md +++ b/guide/src/ops/exp_log.md @@ -1 +1,27 @@ -(including the estimated ones) \ No newline at end of file +# Exponential and Logarithm + +`FBig`/`DBig` provide the exponential, logarithmic, power, and root families, plus the mathematical constants. `CBig` provides the complex analogs of each. + +## Two-layer API + +Like all inexact operations, transcendentals come in two layers (see [types](../types.md)): + +- **Context layer** — `Context` methods take a `&Repr` and return `FpResult>` (a correctly-rounded result or an `FpError`), carrying the rounding direction. They accept an optional `&mut ConstCache` for constant reuse. +- **Convenience layer** — methods on `FBig` (`.exp()`, `.ln()`, …) unwrap to a plain `FBig`, panicking on `Indeterminate`/`OutOfDomain`/`InfiniteInput` and saturating overflow/underflow to `±∞`/`±0`. + +## Real functions + +- Exponential: `exp`, `exp_m1` ($e^x - 1$, accurate near zero). +- Logarithm: `ln`, `ln_1p` ($\ln(1+x)$, accurate near zero). +- Powers and roots: `powi(IBig)`, `powf(&FBig)`, `sqrt`, `cbrt`, `nth_root(&n)`, and `hypot(&other)` ($\sqrt{x^2+y^2}$, overflow-safe). +- Constants: `FBig::pi(precision)` computes π; use [`CachedFBig`](../construct.md#cached-arithmetic-for-fbig) to reuse it across calls. + +(`exp2`/`exp10`/`log2`/`log10` are deferred to a later 0.5.x release.) + +## Complex functions + +`CBig` mirrors the real set with `exp`, `ln`, `sqrt`, `powi`, and `powf`, built on the real implementations. The identities are + +$$\exp(x+iy) = e^x(\cos y + i\sin y), \qquad \log z = \ln|z| + i\,\arg z,$$ + +with `ln`'s principal branch cut on $]-\infty, 0]$ — so the sign of an imaginary zero selects the side of the cut. See [Standards Compliance](../compliance.md) for the full C99 Annex G special-value and branch-cut tables. diff --git a/guide/src/ops/index.md b/guide/src/ops/index.md index e69de29b..d909e6a7 100644 --- a/guide/src/ops/index.md +++ b/guide/src/ops/index.md @@ -0,0 +1,10 @@ +# Operations + +dashu implements a full set of arithmetic, comparison, bitwise, and number-theoretic operations for its numeric types, following standard Rust operator conventions. This section covers each category: + +- [Equality and Comparison](./cmp.md) — `PartialEq`/`Eq`, ordering, magnitude comparison, and hashing. +- [Basic Arithmetics](./basic.md) — `+ - * /` and the rest of the field for each type. +- [Exponential and Logarithm](./exp_log.md) — `exp`, `ln`, powers, roots, and constants, plus the complex analogs. +- [Trigonometric and Hyperbolic Functions](./trig_n_hyper.md) — `sin`/`cos`/`tan` and the hyperbolic family, real and complex. +- [Bit Manipulation](./bit.md) — bitwise operators and `UBig` as a bit vector. +- [Number Theoretic](./num_theory.md) — GCD, extended GCD, and modular arithmetic. diff --git a/guide/src/ops/num_theory.md b/guide/src/ops/num_theory.md index 2399e468..9dcb4731 100644 --- a/guide/src/ops/num_theory.md +++ b/guide/src/ops/num_theory.md @@ -1,5 +1,33 @@ -# Greatest Common Divisor +# Number Theoretic -and extended gcd +`dashu-int` provides greatest-common-divisor and modular-arithmetic primitives. -# Diophantine Approximation +## Greatest common divisor + +The `Gcd` trait (from `dashu-base`) gives `gcd`, and `ExtendedGcd` gives `gcd_ext`, which returns `(gcd, x, y)` with $a\cdot x + b\cdot y = \gcd(a,b)$. + +```rust +use dashu_base::Gcd; +use dashu_int::UBig; + +let a = UBig::from(12u8); +let b = UBig::from(8u8); +assert_eq!((&a).gcd(&b), UBig::from(4u8)); +``` + +## Modular arithmetic + +For repeated operations against a fixed modulus, precompute a `ConstDivisor` and reduce values into `Reduced`. Addition, subtraction, multiplication, exponentiation, and inversion then run against the precomputed modulus, and the result prints in `(mod N)` form. + +```rust +use dashu_int::{UBig, fast_div::ConstDivisor}; + +let ring = ConstDivisor::new(UBig::from(10000u32)); +let x = ring.reduce(12345); +let y = ring.reduce(55443); +assert_eq!(format!("{}", x - y), "6902 (mod 10000)"); +``` + +## Diophantine approximation + +Rational approximation of reals — the simplest rational within a tolerance, continued fractions — lives on `RBig`; see [Conversion](../convert.md#conversion-to-rbig) for `simplest_in` / `nearest_in`. diff --git a/guide/src/ops/trig_n_hyper.md b/guide/src/ops/trig_n_hyper.md new file mode 100644 index 00000000..43c22bc2 --- /dev/null +++ b/guide/src/ops/trig_n_hyper.md @@ -0,0 +1,18 @@ +# Trigonometric and Hyperbolic Functions + +`FBig`/`DBig` and `CBig` provide the trigonometric and hyperbolic functions. They are grouped on one page because the complex circular functions are built from the real circular *and* hyperbolic functions. + +## Real functions + +- Circular: `sin`, `cos`, `tan`, and `sin_cos` (both at once); inverses `asin`, `acos`, `atan`, and the four-quadrant `atan2(y, x)`. +- Hyperbolic: `sinh`, `cosh`, `tanh`, `sinh_cosh`; inverses `asinh`, `acosh`, `atanh`. + +Angles are in radians. `atan2` follows the C99 signed-zero model, which matters for correct branch-cut behavior on the axes. + +## Complex functions + +`CBig` provides the circular family `sin`, `cos`, `tan`, `sin_cos`, `asin`, `acos`, and `atan`. They are evaluated from the real `sin`/`cos` and `sinh`/`cosh` via the identities + +$$\sin(x+iy) = \sin x\cosh y + i\cos x\sinh y, \qquad \cos(x+iy) = \cos x\cosh y - i\sin x\sinh y.$$ + +The inverse functions follow the Kahan signed-zero branch-cut formulation. (Complex-valued hyperbolic functions — `CBig::sinh`, `cosh`, … — are deferred to a later 0.5.x release.) See [Standards Compliance](../compliance.md) for the Annex G special-value and branch-cut tables. From 19652f266857ff986d96e10c46295e12411753aa Mon Sep 17 00:00:00 2001 From: Jacob Zhong Date: Tue, 30 Jun 2026 14:52:03 +0800 Subject: [PATCH 05/21] =?UTF-8?q?Guide=20Batch=204:=20reference=20pages=20?= =?UTF-8?q?=E2=80=94=20faq,=20cheatsheet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - faq.md: why "dashu" (大数 pinyin), a comparison table vs num-bigint/ibig/rug (with a malachite note), known limitations, and the MSRV & feature policy. - cheatsheet.md: dense reference tables for types, construction, conversion, operators, formatting, and key methods (with CBig rows throughout). Co-Authored-By: Claude --- guide/src/cheatsheet.md | 70 +++++++++++++++++++++++++++++++++++++++++ guide/src/faq.md | 34 +++++++++++++++++--- 2 files changed, 99 insertions(+), 5 deletions(-) diff --git a/guide/src/cheatsheet.md b/guide/src/cheatsheet.md index e69de29b..6d6e4a29 100644 --- a/guide/src/cheatsheet.md +++ b/guide/src/cheatsheet.md @@ -0,0 +1,70 @@ +# Cheatsheet + +A dense reference for the dashu numeric types. See the linked pages for detail. + +## Types + +| Type | Crate | Description | Literal | +|------|-------|-------------|---------| +| `UBig` | dashu-int | unsigned integer | `ubig!(123)` | +| `IBig` | dashu-int | signed integer | `ibig!(-123)` | +| `FBig` | dashu-float | float, base 2 by default | `fbig!(0x1.8)` | +| `DBig` | dashu-float | decimal float, base 10 | `dbig!(1.5)` | +| `RBig` | dashu-ratio | rational | `rbig!(22/7)` | +| `CBig` | dashu-cmplx | complex, base 2 by default | `cbig!(1+2i)` | + +## Construction + +| Way | Example | +|-----|---------| +| `From` primitive | `UBig::from(123u32)` | +| parse | `"12.34".parse::()?` | +| from parts | `RBig::from_parts(1.into(), 3u8.into())` | +| literal macro | `dbig!(1.5)`, `cbig!(1+2i)` | +| raw words | `UBig::from_words(&[3, 2, 1])` | + +## Conversion + +Lossless conversions use `From`; potentially-lossy ones use `TryFrom` (which fails on any precision loss). See [Conversion](./convert.md) for the full matrix. + +| From → To | Trait | Notes | +|-----------|-------|-------| +| `UBig` → `IBig` | `From` | | +| `IBig` → `UBig` | `TryFrom` | fails if negative | +| int → `FBig` | `From` | precision inferred from magnitude | +| `FBig` → int | `TryFrom` | fails if fractional or infinite | +| `FBig` → `f32`/`f64` | `.to_f32()` / `.to_f64()` | returns `Rounded` | +| `f32`/`f64` → `FBig` | `TryFrom` | base 2 only | +| real → `CBig` | `From` | imaginary part `+0` | +| `CBig` → `FBig` | `TryFrom` | fails unless imaginary is zero | + +## Operators + +| Type | `+ - * /` | `%` | `<< >>` | `& \| ^ !` | +|------|:---:|:---:|:---:|:---:| +| `UBig` / `IBig` | ✓ | ✓ | ✓ | ✓ | +| `FBig` / `DBig` | ✓ | — | — | — | +| `RBig` | ✓ | — | — | — | +| `CBig` | ✓ | — | — | — | + +## Formatting + +| Type | `Display` | `Debug` | Other | +|------|-----------|---------|-------| +| `UBig`/`IBig` | decimal | head‥tail (+ digits/bits with `#?`) | `Binary`/`Octal`/`Hex`, `in_radix(2..=36)` | +| `FBig`/`DBig` | positional | `sig * base ^ exp` | `LowerExp`/`UpperExp` | +| `RBig` | `num/den` | — | `in_radix`, `in_expanded` | +| `CBig` | `a+bi` | `re:.. im:.. (prec: ..)` | — | + +## Key methods + +| Method | On | Returns | +|--------|-----|---------| +| `.exp()` / `.ln()` / `.sqrt()` | `FBig`, `CBig` | same type | +| `.sin()` / `.cos()` / `.tan()` / `.sin_cos()` | `FBig`, `CBig` | same type | +| `.powi(IBig)` / `.powf(&Self)` | `FBig`, `CBig` | same type | +| `.with_precision(p)` | `FBig` | `Rounded` | +| `.to_decimal()` / `.to_binary()` | `FBig` | `Rounded` / `Rounded` | +| `.conj()` / `.proj()` | `CBig` | `CBig` | +| `.abs()` / `.arg()` / `.norm()` | `CBig` | `FBig` | +| `.gcd(&b)` / `.gcd_ext(&b)` | `UBig`/`IBig` (`Gcd`) | `Self` / `(gcd, x, y)` | diff --git a/guide/src/faq.md b/guide/src/faq.md index 79f94f70..a8bce574 100644 --- a/guide/src/faq.md +++ b/guide/src/faq.md @@ -1,7 +1,31 @@ -# Why is the library called `dashu`? +# FAQ -# Why to use dashu? +## Why is the library called `dashu`? -(list the features of dashu and other crates: num-bigint, ibig, malachite) -(reference: https://rkyv.org/feature-comparison.html?highlight=features#feature-matrix) -(maybe need to split the table to integer, float, rational three sections) +`dashu` is the pinyin romanization of 大数 ("dà shù"), Chinese for *big number*. + +## Why to use `dashu`? + +`dashu` aims to be a Rust-native, ergonomic alternative to GNU GMP + MPFR + MPC: arbitrary-precision integers, floats, rationals, and complex numbers, all in pure Rust with full `no_std` support and arbitrary-base floats. + +Compared with other Rust crates: + +| Crate | Pure Rust | Full `no_std` | Int | Float | Ratio | Complex | +|-------|-----------|---------------|-----|-------|-------|---------| +| **dashu** | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `num-bigint` | ✓ | ✗ | ✓ | ✗ | ✗ | ✗ | +| `ibig` | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ | +| `rug` | ✗ (C/GMP) | ✗ | ✓ | ✓ | ✓ | ✓ | + +`malachite` also offers pure-Rust integers and rationals with a performance focus, but is `std`-oriented and does not cover arbitrary-precision floats or complex numbers. Unlike `rug`, `dashu` has no C dependency — it builds and runs anywhere Rust does, including `no_std` targets. + +## Known limitations + +- **No NaN.** Invalid operations panic at the convenience layer and return `Err(FpError)` at the context layer. Infinities are terminal values, not operands — see [Standards Compliance](./compliance.md). +- **Near-correct rounding.** Transcendentals are rounded within 1 ulp via a guard-digit recipe; a guaranteed-correct Ziv loop is planned for a later release. +- **Complex surface.** `CBig` ships field arithmetic and the elementary transcendentals; complex hyperbolics, `fma`, and several others are deferred to 0.5.x (see the v0.5 release notes). +- **No SIMD-FFT multiplication** yet (planned for v1.0). + +## MSRV and feature policy + +The current MSRV is **1.68**. Third-party integrations follow a versioned-feature convention: stable dependencies use `xxx_vYY` (e.g. `rand_v08`) with an unversioned `xxx` alias pinned to one version, while unstable dependencies alias `xxx` to the newest. See [Cargo Features](./index.md#cargo-features) for the full explanation. From cf4a3c1a7641648feecd2f77d798041b3ab23dc8 Mon Sep 17 00:00:00 2001 From: Jacob Zhong Date: Tue, 30 Jun 2026 14:56:35 +0800 Subject: [PATCH 06/21] Guide Batch 5: KaTeX retrofit + SUMMARY cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Retrofit the genuine math formulas in types.md and compliance.md to KaTeX (significand×base^exponent, the |error|<1 ulp bound, the Kahan branch-cut identity log(-r±i·0)=ln r±iπ, and the arg(0±i∞)=±π/2 values); leave the dense table value-mappings as readable Unicode. - SUMMARY.md: drop the dangling [Complex Numbers](./complex.md) entry; the orphan 1-line stub is removed from disk. Co-Authored-By: Claude --- guide/src/SUMMARY.md | 1 - guide/src/compliance.md | 8 ++++---- guide/src/types.md | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md index 6dcdd30e..c9c1539d 100644 --- a/guide/src/SUMMARY.md +++ b/guide/src/SUMMARY.md @@ -21,4 +21,3 @@ - [Performance](./performance.md) - [Cheatsheet](./cheatsheet.md) - [Standards Compliance](./compliance.md) -- [Complex Numbers](./complex.md) diff --git a/guide/src/compliance.md b/guide/src/compliance.md index fa005f2d..90669830 100644 --- a/guide/src/compliance.md +++ b/guide/src/compliance.md @@ -23,7 +23,7 @@ The reference here is IEEE Std 754™-2008 (ISO/IEC/IEEE 60559:2011). | IEEE 754 requirement | Compliance | Notes | |---------------------|-----------|-------| | Binary and decimal formats | ✅ Supported | `FBig` (binary) and `DBig` = `FBig` (decimal). Other bases are supported via the `const BASE: Word` parameter. | -| Finite non-zero numbers | ✅ | Represented as `significand × BASE^exponent` with unbounded significand. | +| Finite non-zero numbers | ✅ | Represented as $\text{significand} \times \text{BASE}^{\text{exponent}}$ with unbounded significand. | | Signed zero (`±0`) | ✅ | Encoded via exponent sentinels: `+0` ↔ exponent `0`, `-0` ↔ exponent `-1`. Produced by arithmetic, rounding, and cancellations per IEEE 754. | | Signed infinity (`±∞`) | ✅ | Encoded via exponent sentinels: `+∞` ↔ `isize::MAX`, `-∞` ↔ `isize::MIN`. | | NaN | ❌ Deviates | No NaN. Invalid operations panic (at the `FBig` convenience layer) or return `Err(FpError)` (at the `Context` layer). | @@ -58,7 +58,7 @@ The reference here is IEEE Std 754™-2008 (ISO/IEC/IEEE 60559:2011). | IEEE 754 requirement | Compliance | Notes | |---------------------|-----------|-------| | Rounding modes: roundTiesToEven, roundTiesToAway, roundTowardPositive, roundTowardNegative, roundTowardZero | ✅ | All five modes implemented as `HalfEven`, `HalfAway`, `Up`, `Down`, `Zero`. | -| Correct rounding to within 1 ulp | ✅ | All operations guarantee `|error| < 1 ulp`. The `Rounded` type distinguishes exact from inexact results. | +| Correct rounding to within 1 ulp | ✅ | All operations guarantee $|error| < 1\text{ ulp}$. The `Rounded` type distinguishes exact from inexact results. | | Round-to-nearest preserves sign of zero | ✅ | `rounded_to_repr` preserves input sign when rounding collapses a non-zero to zero. | #### Section 5.6 — Sign bit operations @@ -139,13 +139,13 @@ the `Context` layer (and panics at the convenience layer). | C99 Annex G requirement | Compliance | Notes | |---------------------|-----------|-------| -| Branch cuts follow the Kahan signed-zero model | ✅ | e.g. `log(-r ± i·0) = ln r ± i·π`: the sign of the imaginary zero selects the side of the cut. | +| Branch cuts follow the Kahan signed-zero model | ✅ | e.g. $\log(-r \pm i\cdot0) = \ln r \pm i\pi$: the sign of the imaginary zero selects the side of the cut. | | `sqrt(+∞) = +∞` | ✅ | | | `sqrt(-∞) = +0 + i·∞` | ✅ | | | `exp(+∞) = +∞`, `exp(-∞) = +0` | ✅ | | | `exp(0 + i·∞)` → NaN (C) | ⚠️ Partial | Returns `Err(FpError::Indeterminate)`. | | `log(0) = -∞`, `log(+∞) = +∞` | ✅ | | -| `arg(0 + i·∞) = +π/2`, `arg(0 - i·∞) = -π/2` | ✅ | `arg = atan2(im, re)`, reusing `dashu-float`'s Annex-G `atan2` table. | +| $\arg(0 + i\cdot\infty) = +\pi/2$, $\arg(0 - i\cdot\infty) = -\pi/2$ | ✅ | `arg = atan2(im, re)`, reusing `dashu-float`'s Annex-G `atan2` table. | | `abs`/`hypot` overflow-safe modulus | ✅ | Thin composition over `dashu-float`'s `hypot`. | ### Exceptional Conditions diff --git a/guide/src/types.md b/guide/src/types.md index 3ff656e4..4aa1db4e 100644 --- a/guide/src/types.md +++ b/guide/src/types.md @@ -4,7 +4,7 @@ In `dashu` crates, there are standalone types for each kind of numbers with arbi - `dashu_int::UBig` (alias `dashu::Natural`) represents unsigned integers (i.e. natural numbers). - `dashu_int::IBig` (alias `dashu::Integer`) represents (signed) integers. -- `dashu_float::FBig` (alias `dashu::Real`) represents real numbers with floating point representation (`signficand * base ^ exponent`) +- `dashu_float::FBig` (alias `dashu::Real`) represents real numbers with floating point representation ($\text{significand} \times \text{base}^{\text{exponent}}$) - `dashu_float::DBig` (alias `dashu::Decimal`) is a specialization of `FBig` with `base = 10`. - `dashu_ratio::RBig` (alias `dashu::Rational`) represents rational numbers. It has a variant `dashu_ratio::Relaxed`, which also represents a rational number, but it doesn't enforce that the number is in the canonicalized form. - `dashu_cmplx::CBig` (alias `dashu::Complex`) represents complex numbers, built as a pair of `FBig` parts sharing one precision and rounding mode. From caa015693b70c24c0fcc42c4cc68ee5ab862fe6e Mon Sep 17 00:00:00 2001 From: Jacob Zhong Date: Mon, 6 Jul 2026 10:26:55 +0800 Subject: [PATCH 07/21] types.md: restyle Complex section as "Layout of CBig"; add ConstCache + FpResult/CfpResult - Restyle the CBig section as "Layout of `CBig`" to match the Layout of UBig/FBig sections (layout-focused prose, same tone). - Auxiliary Types: broaden the intro (the types span dashu-base/-float/-cmplx) and add ConstCache (the reusable math-constant cache) and FpResult/CfpResult (the context-layer result types, with the FpError variants). Co-Authored-By: Claude --- guide/src/types.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/guide/src/types.md b/guide/src/types.md index 4aa1db4e..26978e64 100644 --- a/guide/src/types.md +++ b/guide/src/types.md @@ -33,19 +33,15 @@ The most fundamental type of the `dashu` libraries is the natural number `UBig`. The layout of `FBig` (and `DBig`) is a little different from other types. An `FBig` instance contains a number representation `dashu_float::Repr` and a context `dashu_float::Context`. The context will be copied every time a new `FBig` is created based on it. The context currently contains the rounding information and the precision associated with this number. The context is kept deliberately lightweight (`Copy` + `Send` + `Sync`): the shared cache for math constants (such as π, ln2, ln10) lives *outside* the context, in the separate [`CachedFBig`](./construct.md#cached-arithmetic-for-fbig) wrapper, so that a plain `FBig` stays cheap to copy and usable in `const`/`static` contexts. Therefore, if you don't want to store the additional context information, you can just store the `Repr` part of the `FBig`. The later operations on the `Repr` can be called with the associated methods of the `Context`, which all takes the reference to a `Repr` instance. However, this could lead to a little overhead in some cases. -## Complex Numbers +## Layout of `CBig` -The `CBig` type (in the `dashu-cmplx` crate) is an arbitrary-precision complex number, and it is generic over a rounding mode `R` and a base `B`, just like `FBig`. A `CBig` instance holds two `Repr` parts — the real part `re` and the imaginary part `im` — over a **single shared** `Context`. +`CBig` (in the `dashu-cmplx` crate) mirrors `FBig`'s `Repr`+`Context` layout, generalized to two parts: a `CBig` instance holds two `Repr` parts — the real part `re` and the imaginary part `im` — over a **single shared** `Context`. Storing one context (rather than wrapping two `FBig`s, each carrying its own) makes the uniform-precision invariant *physical*: there is exactly one precision slot, so `re` and `im` structurally cannot disagree. Each part keeps its own significand length; the shared context holds only the precision cap and the single rounding mode, applied independently to each component. As with `FBig`, the context is `Copy` while the significands are heap-allocated, so `CBig` is `Clone` but not `Copy`. -Storing one context — rather than wrapping two independent `FBig`s (each carrying its own) — makes the uniform-precision invariant *physical*: there is exactly one precision slot, so `re` and `im` structurally cannot disagree. Each part keeps its own significand length; the shared context holds only the precision cap and the single rounding mode, which is applied independently to each component. The result context of `CBig::from_parts(re, im)` is the larger of the two (an unlimited `0` precision is treated as the minimum, so a limited operand always wins), and widening the smaller part to it is exact. - -Because both parts are `Repr`, `CBig` inherits `dashu-float`'s signed-zero / signed-infinity / branch-cut machinery directly. Rounding follows the C99 Annex G / Kahan model that `dashu-float` already implements for reals — see [Standards Compliance](./compliance.md) for the full special-value and branch-cut tables. As with `FBig`, there is **no NaN**: C99 cases that would produce a complex NaN are reported as `FpError` at the context layer (and panic at the convenience layer). - -`CBig` exposes the constants `CBig::ZERO` ($0+0i$), `CBig::ONE` ($1+0i$), and `CBig::I` ($0+1i$, the imaginary unit), all at unlimited precision. There is no `CBig::INFINITY` constant: the complex infinity is the single Riemann point $+\infty+i\cdot0$, produced by `proj` and by overflow. Construction, arithmetic, and the rest of the surface are covered in [Construction and Destruction](./construct.md), [Conversion](./convert.md), and the [Operations](./ops/index.md) pages. +Because both parts are `Repr`, `CBig` reuses `dashu-float`'s signed-zero / signed-infinity / branch-cut machinery unchanged. It follows the C99 Annex G / Kahan model (see [Standards Compliance](./compliance.md)) and, like `FBig`, has **no NaN** — C99 cases that would produce a complex NaN are reported as `FpError` at the context layer. `CBig::from_parts(re, im)` takes the larger of the two operand contexts. Construction, arithmetic, transcendentals, and I/O are covered in [Construction and Destruction](./construct.md), [Conversion](./convert.md), and [Operations](./ops/index.md). # Auxiliary Types -Besides the numeric types defined in separate crates, there are some auxiliary types defined in the crate **dashu-base**. +Besides the numeric types, there are several auxiliary types used across the crates: `Sign` and `Approximation` in **dashu-base**, `ConstCache` and `FpResult` in **dashu-float**, and `CfpResult` in **dashu-cmplx**. ## Sign @@ -60,3 +56,11 @@ The type `Sign` also supports some operations, namely `Neg` and `Mul`. The sign The enum `Approximation` is another commonly used type in `dashu`. It's used when an operation can return inexact values (such as rounding and number conversion). The enum has two variants: `Exact` and `Inexact`, the latter one contains a error term for representing the sign or magnitude of the error caused by inexact operations. When you have an `Approximation` instance, call `.value()`, `.value_ref()` or `unwrap()` to get the operation result, and call `.error()` to get the error term. This struct also support method to work in functional programming style, such as `.map()` and `.and_then()`. + +## ConstCache + +`dashu_float::ConstCache` holds the exact binary-splitting state for the mathematical constants π, ln2, and ln10, so repeated transcendental calls at increasing precision *extend* prior work instead of recomputing from scratch. It is a plain struct of big integers — base-free, `Send` + `Sync` — and a single cache serves any base. `FBig` and `Context` themselves stay `Copy` and carry no cache; the state lives in the separate [`CachedFBig`](./construct.md#cached-arithmetic-for-fbig) wrapper (as `Rc>`), or you can drive a bare `ConstCache` directly. + +## FpResult and CfpResult + +Inexact operations at the context layer return a result type rather than a bare value: `dashu_float::FpResult = Result, FpError>`, where `Rounded` is the [`Approximation`](#approximation) carrying a `Rounding` flag. The complex analog is `dashu_cmplx::CfpResult` (`Result, FpError>`), whose `CRounded` carries one `Rounding` flag per axis. `FpError` reports why an operation could not produce a finite correctly-rounded value: `Overflow`/`Underflow` (saturated to `±∞`/`±0` by the convenience layer), `Indeterminate` (e.g. `0/0`), `OutOfDomain`, and `InfiniteInput`. The convenience-layer methods unwrap these — saturating overflow/underflow and panicking on the rest. From 45694c8e628a00f6cdf2da1246ea265b3e588e5b Mon Sep 17 00:00:00 2001 From: Jacob Zhong Date: Mon, 6 Jul 2026 10:44:20 +0800 Subject: [PATCH 08/21] =?UTF-8?q?Guide:=20demote=20all=20page=20headings?= =?UTF-8?q?=20by=20one=20level=20(H1=20=E2=86=92=20H2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The page-title H1s caused formatting issues in mdBook's theme, so the top heading of every sub-document is now H2 (SUMMARY.md untouched). All sub-headings shift down one level too, preserving the hierarchy. Internal links are unaffected — anchors derive from heading text, not level. Applied with a fence-aware transform; no code-block content was changed (verified: zero `#`-leading lines exist inside any code fence). Co-Authored-By: Claude --- guide/src/cheatsheet.md | 14 +++++++------- guide/src/compliance.md | 34 +++++++++++++++++----------------- guide/src/construct.md | 24 ++++++++++++------------ guide/src/convert.md | 14 +++++++------- guide/src/faq.md | 10 +++++----- guide/src/index.md | 10 +++++----- guide/src/io/index.md | 2 +- guide/src/io/interop.md | 8 ++++---- guide/src/io/parse.md | 10 +++++----- guide/src/io/print.md | 16 ++++++++-------- guide/src/io/serialize.md | 8 ++++---- guide/src/ops/basic.md | 12 ++++++------ guide/src/ops/bit.md | 8 ++++---- guide/src/ops/cmp.md | 10 +++++----- guide/src/ops/exp_log.md | 8 ++++---- guide/src/ops/index.md | 2 +- guide/src/ops/num_theory.md | 8 ++++---- guide/src/ops/trig_n_hyper.md | 6 +++--- guide/src/performance.md | 6 +++--- guide/src/types.md | 22 +++++++++++----------- 20 files changed, 116 insertions(+), 116 deletions(-) diff --git a/guide/src/cheatsheet.md b/guide/src/cheatsheet.md index 6d6e4a29..eb8a2c66 100644 --- a/guide/src/cheatsheet.md +++ b/guide/src/cheatsheet.md @@ -1,8 +1,8 @@ -# Cheatsheet +## Cheatsheet A dense reference for the dashu numeric types. See the linked pages for detail. -## Types +### Types | Type | Crate | Description | Literal | |------|-------|-------------|---------| @@ -13,7 +13,7 @@ A dense reference for the dashu numeric types. See the linked pages for detail. | `RBig` | dashu-ratio | rational | `rbig!(22/7)` | | `CBig` | dashu-cmplx | complex, base 2 by default | `cbig!(1+2i)` | -## Construction +### Construction | Way | Example | |-----|---------| @@ -23,7 +23,7 @@ A dense reference for the dashu numeric types. See the linked pages for detail. | literal macro | `dbig!(1.5)`, `cbig!(1+2i)` | | raw words | `UBig::from_words(&[3, 2, 1])` | -## Conversion +### Conversion Lossless conversions use `From`; potentially-lossy ones use `TryFrom` (which fails on any precision loss). See [Conversion](./convert.md) for the full matrix. @@ -38,7 +38,7 @@ Lossless conversions use `From`; potentially-lossy ones use `TryFrom` (which fai | real → `CBig` | `From` | imaginary part `+0` | | `CBig` → `FBig` | `TryFrom` | fails unless imaginary is zero | -## Operators +### Operators | Type | `+ - * /` | `%` | `<< >>` | `& \| ^ !` | |------|:---:|:---:|:---:|:---:| @@ -47,7 +47,7 @@ Lossless conversions use `From`; potentially-lossy ones use `TryFrom` (which fai | `RBig` | ✓ | — | — | — | | `CBig` | ✓ | — | — | — | -## Formatting +### Formatting | Type | `Display` | `Debug` | Other | |------|-----------|---------|-------| @@ -56,7 +56,7 @@ Lossless conversions use `From`; potentially-lossy ones use `TryFrom` (which fai | `RBig` | `num/den` | — | `in_radix`, `in_expanded` | | `CBig` | `a+bi` | `re:.. im:.. (prec: ..)` | — | -## Key methods +### Key methods | Method | On | Returns | |--------|-----|---------| diff --git a/guide/src/compliance.md b/guide/src/compliance.md index 90669830..8128172a 100644 --- a/guide/src/compliance.md +++ b/guide/src/compliance.md @@ -1,4 +1,4 @@ -# Standards Compliance +## Standards Compliance This page documents where `dashu`'s numeric types conform to the relevant standards — and where they intentionally deviate. There are two aspects: @@ -12,13 +12,13 @@ The common thread: dashu types are **arbitrary-precision**, so fixed-width-encod standard's rules natural to satisfy, they are satisfied; where they conflict with the arbitrary-precision / no-NaN model, the deviation is noted. -## `dashu-float` and IEEE 754-2008 +### `dashu-float` and IEEE 754-2008 The reference here is IEEE Std 754™-2008 (ISO/IEC/IEEE 60559:2011). -### Data Model +#### Data Model -#### Section 3 — Floating-point formats +##### Section 3 — Floating-point formats | IEEE 754 requirement | Compliance | Notes | |---------------------|-----------|-------| @@ -30,9 +30,9 @@ The reference here is IEEE Std 754™-2008 (ISO/IEC/IEEE 60559:2011). | Subnormals | N/A | Arbitrary-precision significands eliminate the need for subnormals. Any non-zero number is normalized. | | Fixed-width encoding | N/A | No fixed bit widths; significands are unbounded `IBig` integers. | -### Arithmetic Operations +#### Arithmetic Operations -#### Section 5 — Operations +##### Section 5 — Operations | IEEE 754 requirement | Compliance | Notes | |---------------------|-----------|-------| @@ -53,7 +53,7 @@ The reference here is IEEE Std 754™-2008 (ISO/IEC/IEEE 60559:2011). | Cancellation under roundTowardNegative → `-0` | ✅ | `cancel_zero` in add.rs produces `-0` when `R::IS_ROUND_TOWARD_NEGATIVE`. | | Exact subtraction cancels to `-0` only under directed rounding | ✅ | IEEE 754 §6.3: `(-3) + 3` = `+0` under roundTiesToEven/Up, `-0` under Down. | -#### Section 5.3 — Rounding +##### Section 5.3 — Rounding | IEEE 754 requirement | Compliance | Notes | |---------------------|-----------|-------| @@ -61,7 +61,7 @@ The reference here is IEEE Std 754™-2008 (ISO/IEC/IEEE 60559:2011). | Correct rounding to within 1 ulp | ✅ | All operations guarantee $|error| < 1\text{ ulp}$. The `Rounded` type distinguishes exact from inexact results. | | Round-to-nearest preserves sign of zero | ✅ | `rounded_to_repr` preserves input sign when rounding collapses a non-zero to zero. | -#### Section 5.6 — Sign bit operations +##### Section 5.6 — Sign bit operations | IEEE 754 requirement | Compliance | Notes | |---------------------|-----------|-------| @@ -70,7 +70,7 @@ The reference here is IEEE Std 754™-2008 (ISO/IEC/IEEE 60559:2011). | `signum(±0)` = `+0` | ✅ | Returns `+0` for both `+0` and `-0` (signum collapses the sign of zero). | | `sign()` distinguishes `+0` from `-0` | ✅ | `Repr::sign()` returns `Negative` for `-0`. | -### Conversions +#### Conversions | IEEE 754 requirement | Compliance | Notes | |---------------------|-----------|-------| @@ -81,7 +81,7 @@ The reference here is IEEE Std 754™-2008 (ISO/IEC/IEEE 60559:2011). | Int-to-float conversion exact for representable integers | ✅ | | | Float-to-int overflows saturate (per Rust convention) | N/A | Rust's `TryFrom` returns an error on overflow; `ToPrimitive` returns `None`. | -### Exceptional Conditions +#### Exceptional Conditions | IEEE 754 requirement | Compliance | Notes | |---------------------|-----------|-------| @@ -91,7 +91,7 @@ The reference here is IEEE Std 754™-2008 (ISO/IEC/IEEE 60559:2011). | Underflow → `±0` (no trap) | ✅ | Same. | | Inexact flag | ⚠️ Partial | The `Rounded` type carries `Exact`/`Inexact(T, Rounding)` to signal whether rounding occurred, but there is no sticky flag mechanism. | -### Summary (dashu-float) +#### Summary (dashu-float) | Category | Status | |----------|--------| @@ -104,7 +104,7 @@ The reference here is IEEE Std 754™-2008 (ISO/IEC/IEEE 60559:2011). | Subnormals | N/A (unbounded precision) | | Exception flags | ⚠️ Rounded type signals exact/inexact, no sticky flags | -## `dashu-cmplx` and C99 Annex G +### `dashu-cmplx` and C99 Annex G `CBig` is a pair of `Repr` parts (real, imaginary) over a single shared precision and rounding mode. It targets C99 Annex G (IEC 60559-compatible complex arithmetic) for the common functionality, @@ -112,7 +112,7 @@ reusing `dashu-float`'s signed-zero / signed-infinity / branch-cut machinery for `FBig`, there is **no NaN**: C99 cases that would produce a complex NaN are mapped to `FpError` at the `Context` layer (and panics at the convenience layer). -### Data Model (§G.2) +#### Data Model (§G.2) | C99 Annex G requirement | Compliance | Notes | |---------------------|-----------|-------| @@ -122,7 +122,7 @@ the `Context` layer (and panics at the convenience layer). | A single complex infinity (Riemann point) | ✅ | `proj` collapses any part-infinite value to `+∞ + i·0`; overflow yields both parts `+∞`. | | Complex NaN | ❌ Deviates | No NaN. NaN-producing cases map to `FpError` (`Context`) / panic (convenience layer). | -### Arithmetic (§G.5) +#### Arithmetic (§G.5) | C99 Annex G requirement | Compliance | Notes | |---------------------|-----------|-------| @@ -135,7 +135,7 @@ the `Context` layer (and panics at the convenience layer). | `finite/∞`, `0/finite` → `0` | ✅ | | | `1/0 → ∞`, `1/∞ → 0` (inverse) | ✅ | | -### Transcendentals and branch cuts (§G.6) +#### Transcendentals and branch cuts (§G.6) | C99 Annex G requirement | Compliance | Notes | |---------------------|-----------|-------| @@ -148,7 +148,7 @@ the `Context` layer (and panics at the convenience layer). | $\arg(0 + i\cdot\infty) = +\pi/2$, $\arg(0 - i\cdot\infty) = -\pi/2$ | ✅ | `arg = atan2(im, re)`, reusing `dashu-float`'s Annex-G `atan2` table. | | `abs`/`hypot` overflow-safe modulus | ✅ | Thin composition over `dashu-float`'s `hypot`. | -### Exceptional Conditions +#### Exceptional Conditions | C99 Annex G requirement | Compliance | Notes | |---------------------|-----------|-------| @@ -156,7 +156,7 @@ the `Context` layer (and panics at the convenience layer). | Domain error (e.g. even root of a negative value, out-of-range inverse trig) | ❌ Deviates | `Err(FpError::OutOfDomain)` / panic, rather than a NaN result. | | Each component rounded independently to the shared mode | ✅ | Near-correctly rounded per axis (a guaranteed-correct Ziv loop is deferred to 0.5.x). | -### Summary (dashu-cmplx) +#### Summary (dashu-cmplx) | Category | Status | |----------|--------| diff --git a/guide/src/construct.md b/guide/src/construct.md index f2c6b09d..9749f1d3 100644 --- a/guide/src/construct.md +++ b/guide/src/construct.md @@ -1,6 +1,6 @@ There are multiple ways to construct and deconstruct the numeric types, which are listed below. These constructors are used for directly compose the numbers from its components. To construct from alternative representations, please refer to the [Input and Output](./io/index.md) and [Conversion](convert.md) sections. -# Constants +## Constants For all the numeric types, there are several constants associated with the type. You can use them to construct an instance, or directly use them with binary operators. These constants includes: @@ -10,13 +10,13 @@ For all the numeric types, there are several constants associated with the type. - `RBig`: `::ZERO`, `::ONE`, `::NEG_ONE` - `CBig`: `::ZERO` ($0+0i$), `::ONE` ($1+0i$), `::I` ($0+1i$) -# Raw Constructor for `UBig` +## Raw Constructor for `UBig` For `UBig`, it can be constructed from a slice of [`Word`](./types.md#word)s, using the `::from_words()` method. The words must be arranged in little-endian order, i.e. the first word should represent the least significant part of the number. If then integer you want to construct is small, then you can also use the `::from_word()` and `::from_dword()` methods, which can be called from a `const` context. To deconstruct a `UBig`, currently we don't support taking the ownership of the words stored in a `UBig`. You can only access them using the `.as_words()` method, which returns a reference to the words. In future, when the memory layout of the `UBig` is stablized, it's possible to add a deconstructor that giving the ownership of the word to prevent unnecessary copying. -# Construct from Parts +## Construct from Parts For other numeric types, they are usually composed by several parts. And you can construct them using the `::from_parts()` and `::from_parts_const()` method. The latter one can be called from a `const` context, but the size of the components is limited when using `::from_parts_const()`. @@ -36,7 +36,7 @@ It's worth noting that, the constructors for `FBig` and `DBig` also determines t To deconstruct these numeric types, use the `::into_parts()` functions to get the components without copying. However for `FBig`/`DBig`, you should use the `.into_repr()` to get the underlying representation `Repr`, and then use the `.into_parts()` method of `Repr` to get the magnitude and mantissa. -# `dashu-macros` +## `dashu-macros` We also provide a convenient and efficient way to create large numbers from literals through the macros `ubig!`/`ibig!`/`fbig!`/`dbig!`/`rbig!`/`cbig!`. These macros can be obtained directly from the `dashu-macros` crate or from the `dashu` meta crate. The `cbig!` macro accepts the same algebraic form as `CBig`'s `FromStr` (e.g. `cbig!(3+4i)`, `cbig!(-i)`) or a `re, im` pair (e.g. `cbig!(3, 4)`). @@ -46,7 +46,7 @@ When the number doesn't have a high precision, these macros can be used in a `co Please refer to [the docs of `dashu-macros`](https://docs.rs/dashu-macros/latest/dashu_macros/) for detailed usage of these macros. -# Cached Arithmetic for FBig +## Cached Arithmetic for FBig The [`CachedFBig`] type is an [`FBig`] that carries a shared handle to a `Rc>`. The cache stores exact binary-splitting state for @@ -54,7 +54,7 @@ mathematical constants (π, ln2, ln10), so that transcendental operations (`ln`, `exp`, `sin`, `cos`, …, `pi`) reuse and progressively extend prior work instead of recomputing from scratch. -## Creation +### Creation A `CachedFBig` is created by attaching a cache handle to an `FBig`: @@ -89,7 +89,7 @@ To drop the cache and get back a plain `FBig`, use `into_fbig()` or the let plain: FBig = cached.into(); // or cached.into_fbig() ``` -## Cache sharing +### Cache sharing Binary operations between `CachedFBig` values preserve the cache handle in the result: `(a + b).ln().exp()` keeps extending the same cache throughout. @@ -108,7 +108,7 @@ let result = cached + 3u8; // CachedFBig, cache preserved let result = 10i32 * cached; // CachedFBig, cache preserved ``` -## Inspecting and clearing the cache +### Inspecting and clearing the cache Use `cache()` to borrow the cache read-only and inspect its size: @@ -125,7 +125,7 @@ cached.clear_cache(); assert_eq!(cached.cache().total_terms(), 0); ``` -## More constructors and accessors +### More constructors and accessors Beyond `into_cached` / `with_cache` / `From`, `CachedFBig` mirrors the rest of `FBig`'s construction surface while preserving the cache handle: @@ -134,7 +134,7 @@ Beyond `into_cached` / `with_cache` / `From`, `CachedFBig` mirrors the res - `as_fbig()` — borrow the inner `FBig` immutably (cheap; no cache detach). - `from_repr(repr, context, cache)` / `into_repr()` — the raw-repr constructor/destructor that share a specific cache handle. -## Computing constants directly +### Computing constants directly The cache stores exact binary-splitting state for the constants π, ln2, and ln10, so the methods that produce them reuse and progressively extend prior work rather than recomputing from scratch. On `CachedFBig`, π is a single call: @@ -165,11 +165,11 @@ let ln10 = cache.ln10::<10, HalfAway>(100); `ln_base::(precision)` dispatches to the cached ln2 / ln10 when `B` is 2 or 10 (or a power of two), and falls back to a direct `ln(B)` otherwise. -## Thread safety +### Thread safety `CachedFBig` carries its cache as `Rc>`, so it is **`!Send + !Sync`** — a cached value cannot move across threads. `FBig` itself stays `Copy + Send + Sync` (which is why `static_fbig!` keeps working); only the cached wrapper is non-thread-safe. `ConstCache` is a plain struct of big integers and is itself `Send + Sync`, so to share one cache across threads, wrap a `ConstCache` (or a `CachedFBig`) in `Arc>`. The underlying `Context` methods accept `Option<&mut ConstCache>` regardless of the container, so this needs no API change. -## Worked example: reusing constants across a chain +### Worked example: reusing constants across a chain Because every value-producing operation preserves the cache handle, a chain of transcendentals reuses the same constants throughout. Building several results from one shared handle pays for each constant once: diff --git a/guide/src/convert.md b/guide/src/convert.md index 7d9fc86f..e887fad3 100644 --- a/guide/src/convert.md +++ b/guide/src/convert.md @@ -2,7 +2,7 @@ Dashu supports a complete set of conversions, including conversions among arbitr Note that a general principle of implementations of `TryFrom` in `dashu` is that, `TryFrom` should succeed only when the conversion is lossless. Any precision loss during the conversion should cause the `TryFrom` to return an `Err`. -# Conversion among Types +## Conversion among Types Most of the time, you can use `From`/`Into`/`TryFrom`/`TryInto` to convert between these types. When the conversion is fallible, only `TryFrom` and `TryInto` will be implemented. Below is a table of conversions between arbitrary precision types using these traits, where the columns are source types, and rows are destination types. @@ -35,7 +35,7 @@ Another useful conversion is `UBig::as_ibig()`. Due to the fact that `UBig` and Besides these methods designed for conversions, the constructors and destructors can also be used for the purpose of type conversion, especially from compound types to its parts. Please refer to the [Construction and Destruction](./construct.md#Construct_from_Parts) page for this approach. -# Conversion between Big Numbers and Primitives +## Conversion between Big Numbers and Primitives All the numeric types in the `dashu` crates support conversion from and to primitive types. @@ -64,11 +64,11 @@ In the table above, `.to_f*()` denotes `.to_f32()` and `.to_f64()`, similarly `. The conversions from and to primitive numbers are also implemented for the `dashu_float::Repr` type. Especially `.to_f32()` and `.to_f64()` are implemented which follows the default IEEE rounding mode. -## Conversion for FBig/DBig +### Conversion for FBig/DBig Conversions involving `FBig`/`DBig` are richer than for the integer types, because a floating-point number carries three independent knobs: a **base**, a **precision** (a cap on the number of significant digits), and a **rounding mode**. Most conversions therefore come in two flavors — an infallible `From`/`Into` when no information is lost, and a fallible `TryFrom`/`TryInto` when exactness is required. -## Conversion to different base / precision / rounding mode +### Conversion to different base / precision / rounding mode The base, precision, and rounding mode are changed independently: @@ -92,7 +92,7 @@ For the common binary ↔ decimal hops, two shortcuts pick the rounding mode for > These methods panic if the associated context has **unlimited precision** and the conversion cannot be done losslessly — set a precision first. -## Conversion to integers or primitive floats +### Conversion to integers or primitive floats Converting *into* `FBig` from `UBig`/`IBig` (or any primitive integer) infers the precision from the magnitude: the result precision equals the number of significant base-`B` digits of the integer. @@ -109,13 +109,13 @@ assert_eq!(DBig::from_str("1.234")?.to_int(), Inexact(1.into(), NoOp)); To a primitive float, `to_f32()` / `to_f64()` return `Rounded` / `Rounded` carrying the rounding direction; they never fail (overflow yields `±∞`, infinities map to infinities). The reverse — `TryFrom`/`TryFrom for FBig` — is **base-2 only** (it is almost always lossy in any other base); to reach a non-binary `FBig`, convert to base 2 first and then call `with_base()`. NaN is rejected with `ConversionError::OutOfBounds`. -## Conversion to RBig +### Conversion to RBig With the optional `dashu-float` feature enabled on `dashu-ratio`, `TryFrom for RBig` succeeds only when the float is exactly rational-representable, and `RBig::to_float()` is the rounding-aware path in the other direction. For approximating a float by a *simple* rational (the smallest numerator/denominator within a tolerance), use `simplest_from_f32` / `simplest_from_f64`, or the interval queries `simplest_in`, `nearest_in`, `next_up`, and `next_down` on `FBig`/`DBig` — these treat the float's own rounding interval as the search bound. -## Conversion for CBig +### Conversion for CBig A `CBig` is reached losslessly from any real value: `From`, `From`, and `From` embed the value as the real part with imaginary `+0` (exact, unlimited precision). The inverse is fallible — `TryFrom for FBig` extracts the real part only when the imaginary part is zero (both `±0` count), and `TryFrom for IBig` further requires the real part to be integer-valued. Both compose the `CBig → FBig → IBig` chain, mirroring `FBig`'s own `From`/`TryFrom` split. diff --git a/guide/src/faq.md b/guide/src/faq.md index a8bce574..e31ec5f0 100644 --- a/guide/src/faq.md +++ b/guide/src/faq.md @@ -1,10 +1,10 @@ -# FAQ +## FAQ -## Why is the library called `dashu`? +### Why is the library called `dashu`? `dashu` is the pinyin romanization of 大数 ("dà shù"), Chinese for *big number*. -## Why to use `dashu`? +### Why to use `dashu`? `dashu` aims to be a Rust-native, ergonomic alternative to GNU GMP + MPFR + MPC: arbitrary-precision integers, floats, rationals, and complex numbers, all in pure Rust with full `no_std` support and arbitrary-base floats. @@ -19,13 +19,13 @@ Compared with other Rust crates: `malachite` also offers pure-Rust integers and rationals with a performance focus, but is `std`-oriented and does not cover arbitrary-precision floats or complex numbers. Unlike `rug`, `dashu` has no C dependency — it builds and runs anywhere Rust does, including `no_std` targets. -## Known limitations +### Known limitations - **No NaN.** Invalid operations panic at the convenience layer and return `Err(FpError)` at the context layer. Infinities are terminal values, not operands — see [Standards Compliance](./compliance.md). - **Near-correct rounding.** Transcendentals are rounded within 1 ulp via a guard-digit recipe; a guaranteed-correct Ziv loop is planned for a later release. - **Complex surface.** `CBig` ships field arithmetic and the elementary transcendentals; complex hyperbolics, `fma`, and several others are deferred to 0.5.x (see the v0.5 release notes). - **No SIMD-FFT multiplication** yet (planned for v1.0). -## MSRV and feature policy +### MSRV and feature policy The current MSRV is **1.68**. Third-party integrations follow a versioned-feature convention: stable dependencies use `xxx_vYY` (e.g. `rand_v08`) with an unversioned `xxx` alias pinned to one version, while unstable dependencies alias `xxx` to the newest. See [Cargo Features](./index.md#cargo-features) for the full explanation. diff --git a/guide/src/index.md b/guide/src/index.md index 09557ca0..197d1a96 100644 --- a/guide/src/index.md +++ b/guide/src/index.md @@ -1,4 +1,4 @@ -# The user guide for `dashu` +## The user guide for `dashu` Welcome to the `dashu` user guide! `dashu` is a library set of arbitrary precision numbers (aka. big numbers) implemented in Rust. @@ -6,7 +6,7 @@ The book is a companion to [`dashu`'s API docs](https://docs.rs/dashu/latest/das Please choose from the chapters on the left to jump to individual topics. -## Philosophy and Features +### Philosophy and Features `dashu` is intended to be your go-to library for crafting some algorithms that involves arbitrary precision numbers, or build a rusty tool that relies on arbitrary precision numbers. It's built from scratch with rust, and provides user-friendly and rust-idiomatic APIs. It might not be the fastest library for your high-precision calculation loads, but it's designed to be fast enough so that you seldomly feel dragged back by it. @@ -15,7 +15,7 @@ To make it useful for every Rust user, it features: - Focus on ergonomics & readability, and then efficiency. - Current MSRV is 1.68 -## The meta crate +### The meta crate The crate `dashu` is a meta crate that exposes all the functionalities of the subcrates (`dashu-base`, `dashu-int`, `dashu-float`, `dashu-ratio` and `dashu-macros`). Each subcrate becomes a module in `dashu`, for example, `dashu-int` is re-exported as `dashu::int`. Besides, it creates more readable aliases for the numeric types: - `dashu::Natural` = `dashu::int::UBig` = `dashu_int::UBig` @@ -26,7 +26,7 @@ The crate `dashu` is a meta crate that exposes all the functionalities of the su In this guide, we will use the original names of the numeric types (i.e. `XBig`), but the explanations are also applicable to these re-exported types. -## Cargo Features +### Cargo Features Dashu has several optional features defined for cargo that supports various third-party crates. Most of them are not enabled by default. Specially, we use a special naming rule for the features. - For feature dependencies with stable versions (reached v1.0), we will use `xxx_vyy` to represent its major versions, and `xxx` pointing to one of the major versions. Changing the version which `xxx` is pointing to is regarded as a break change in `dashu` (requiring major version bump). Therefore, when you dependends on `dashu` with these stable features, additional implementations for newer versions in `dashu` will not cause any issues in your code. @@ -36,6 +36,6 @@ Dashu has several optional features defined for cargo that supports various thir In your Cargo.toml, if you enable `dashu/diesel`, `dashu/diesel2` or `dashu/rand_v07`, there won't be any risk of break changes when `dashu` updates the support for `diesel` v3 or `rand` v0.9 in future. However the risk exists if you enable `rand` instead of `rand_v08`, because `rand` might point to `rand_v09` in future. -## License +### License Licensed under either [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) or [MIT license](https://opensource.org/licenses/MIT) at your option. diff --git a/guide/src/io/index.md b/guide/src/io/index.md index cde2ff1e..aaffc9f6 100644 --- a/guide/src/io/index.md +++ b/guide/src/io/index.md @@ -1,4 +1,4 @@ -# Input and Output +## Input and Output dashu's numeric types participate in Rust's standard formatting and parsing traits, plus a few dashu-specific APIs for radix conversion, positional expansion, and byte-level serialization. This section covers: diff --git a/guide/src/io/interop.md b/guide/src/io/interop.md index 874a78f2..2cf8fa55 100644 --- a/guide/src/io/interop.md +++ b/guide/src/io/interop.md @@ -1,8 +1,8 @@ -# Interoperability +## Interoperability Besides the standard formatting and parsing traits, `dashu-int` exposes lower-level access to a `UBig`'s raw representation, for interoperating with other libraries or building custom (de)serialization. -## Digit access +### Digit access `UBig::to_digits(base)` returns the number's digits in any base `2..=Word::MAX` (most-significant first, stored as `Word`), and `UBig::from_digits(base, &digits)` reconstructs it. This generalizes `in_radix` (which is limited to base 2–36 for string output) to arbitrary bases and word-sized digits. @@ -14,11 +14,11 @@ let digits = n.to_digits(16); // [1, 2, 3, 4], most-significant first assert_eq!(UBig::from_digits(16, &digits)?, n); ``` -## Byte access +### Byte access `to_le_bytes` / `to_be_bytes` and `from_le_bytes` / `from_be_bytes` give a portable, explicit-endianness byte representation — see [Serialization](./serialize.md). -## Word access +### Word access `UBig::from_words(&[w0, w1, …])` builds a value from little-endian words, and `.as_words()` borrows the underlying word slice without copying. This is the closest to the raw in-memory form. diff --git a/guide/src/io/parse.md b/guide/src/io/parse.md index fc1af083..f226b81b 100644 --- a/guide/src/io/parse.md +++ b/guide/src/io/parse.md @@ -1,8 +1,8 @@ -# Parsing +## Parsing Every numeric type implements `FromStr`, so values can be built with `"...".parse()?` or `T::from_str(...)`. Underscore separators are allowed in all numeric literals. -## Parsing Integers +### Parsing Integers `UBig::from_str` / `IBig::from_str` accept an optional sign followed by decimal digits. For other bases use `from_str_radix(s, radix)` (radix 2–36); it recognizes a `0x`/`0o`/`0b` prefix independently of the `radix` argument. @@ -14,7 +14,7 @@ assert_eq!(UBig::from_str("12345")?, UBig::from(12345u16)); assert_eq!(IBig::from_str_radix("-1aff", 16)?, IBig::from(-0x1aff)); ``` -## Parsing Floats +### Parsing Floats `FBig`/`DBig` `FromStr` reads the significand in the value's native base, with the exponent in one of these forms: @@ -35,7 +35,7 @@ assert_eq!(format!("{:e}", DBig::from_str("6.022e23")?), "6.022e23"); assert_eq!(DBig::from_str("-0.0123456789")?.to_string(), "-0.0123456789"); ``` -## Parsing Rationals +### Parsing Rationals `RBig::from_str` accepts `numerator/denominator`, or just a numerator (denominator defaults to 1). `from_str_radix` parses both parts in the given base; a `0x`/`0o`/`0b` prefix must be consistent between them. @@ -46,7 +46,7 @@ use core::str::FromStr; assert_eq!(RBig::from_str("22/7")?.to_string(), "22/7"); ``` -## Parsing Complex +### Parsing Complex `CBig` `FromStr` accepts the same algebraic $a+bi$ grammar that `Display` emits: an optional real term plus an optional signed imaginary term (at least one required); a unit coefficient may be omitted (`i`, `-i`). The MPC-style parenthesized form `(re im)` is **not** accepted. diff --git a/guide/src/io/print.md b/guide/src/io/print.md index 04526bbf..449f612f 100644 --- a/guide/src/io/print.md +++ b/guide/src/io/print.md @@ -1,8 +1,8 @@ -# Printing +## Printing `UBig` and `IBig` support the full set of Rust standard formatter traits: `Display`, `Debug`, `Binary`, `Octal`, `LowerHex`, `UpperHex`. The float, rational, and complex types support `Display` and `Debug`, with extra radix/positional helpers described below. All of them honor the sign, width, fill, padding, and alignment options of `Formatter`. -## Integer Formatting +### Integer Formatting `Display` renders a `UBig`/`IBig` in decimal. The `Binary`, `Octal`, `LowerHex`, and `UpperHex` traits render in base 2/8/16, with the `#` flag adding the conventional `0b`/`0o`/`0x`/`0X` prefix. For any other radix, use `in_radix(r)` (base 2–36); its `#` flag uppercases digits above 9. @@ -18,13 +18,13 @@ assert_eq!(format!("{}", n.in_radix(16)), "ff"); assert_eq!(format!("{:#}", n.in_radix(16)), "FF"); ``` -## Debug Print +### Debug Print The `Debug` implementation uses a compact **head‥tail** format for large integers: it prints the most significant digits, a `..` separator, and the least significant digits, omitting the middle. For small integers that fit in a single `Word` or `DoubleWord` the full number is shown without truncation. There are two forms, controlled by the formatter flags: -### Simple form (`{:?}`) +#### Simple form (`{:?}`) Shows the truncated head‥tail representation. @@ -48,7 +48,7 @@ assert_eq!( The number of digits shown on each end depends on the `Word` size — on 64-bit targets it is 19 decimal digits at each end (one word's worth), on 32-bit targets it is 9 digits. -### Verbose form (`{:#?}`) +#### Verbose form (`{:#?}`) Adds `(digits: N, bits: M)` after the head‥tail representation, showing the total digit count and bit length. @@ -64,7 +64,7 @@ if Word::BITS == 64 { } ``` -## Float Formatting +### Float Formatting `FBig`/`DBig` `Display` renders the significand with the radix point positioned by the exponent — the natural positional form, not scientific. The formatter precision option rounds to that many fractional digits. @@ -86,7 +86,7 @@ assert_eq!(format!("{:e}", DBig::from_str("1234.5")?), "1.2345e3"); assert_eq!(format!("{:E}", DBig::from_str("1234.5")?), "1.2345E3"); ``` -## Rational Formatting +### Rational Formatting `RBig`/`Relaxed` `Display` renders as `numerator/denominator`, or just the numerator when the denominator is `1`. The `Binary`/`Octal`/`LowerHex`/`UpperHex` traits and `in_radix(r)` format both parts in the given base. @@ -108,7 +108,7 @@ assert_eq!(format!("{:.4}", x.in_expanded()), "0.3333"); assert_eq!(format!("{:#}", x.in_expanded()), "0.(3)"); ``` -## Complex Formatting +### Complex Formatting `CBig` `Display` uses the algebraic $a+bi$ notation: the imaginary term always carries an explicit sign, a unit coefficient is elided (`i`, not `1i`), and a zero imaginary part is omitted. `Debug` prints `re: im: (prec:

)`. diff --git a/guide/src/io/serialize.md b/guide/src/io/serialize.md index 3e1ea141..aa1be213 100644 --- a/guide/src/io/serialize.md +++ b/guide/src/io/serialize.md @@ -1,4 +1,4 @@ -# Serialization +## Serialization ```text The layout for serialized numbers is protected by semver. A change to the layout is considered a breaking change and a new major version will be published. @@ -6,7 +6,7 @@ The layout for serialized numbers is protected by semver. A change to the layout dashu offers three layers of (de)serialization for its integer and float types, chosen by how portable or fast the format must be. -## Conversion to Bytes +### Conversion to Bytes `UBig` and `IBig` convert to and from explicit-endianness byte sequences via `to_le_bytes` / `to_be_bytes` and `from_le_bytes` / `from_be_bytes`. These are portable, layout-stable formats suitable for binary interchange. @@ -18,10 +18,10 @@ let bytes = n.to_le_bytes(); assert_eq!(UBig::from_le_bytes(&bytes), n); ``` -## Serialization with `serde` +### Serialization with `serde` With the `serde` feature enabled, every numeric type implements `Serialize` / `Deserialize`. The human-readable form (when `is_human_readable()` is true) is a string, for easy use with JSON/TOML; the compact binary form is used otherwise. Only the binary form's layout is semver-protected. -## Serialization with `rkyv` +### Serialization with `rkyv` With the `rkyv` feature enabled, zero-copy (de)serialization is available for the integer types — fastest for same-architecture scenarios, at the cost of a less portable layout. diff --git a/guide/src/ops/basic.md b/guide/src/ops/basic.md index ed96fc6b..10de4427 100644 --- a/guide/src/ops/basic.md +++ b/guide/src/ops/basic.md @@ -1,8 +1,8 @@ -# Basic Arithmetics +## Basic Arithmetics The standard arithmetic operators are implemented for all numeric types, for both owned and borrowed operands. The behavior of division and remainder differs by type. -## Integer Arithmetic +### Integer Arithmetic `UBig` and `IBig` support `+`, `-`, `*`, `/`, and `%`. Integer division rounds toward zero, and the remainder takes the sign of the dividend (the C/Rust convention). For Euclidean division (non-negative remainder) use the `DivRemEuclid` / `RemEuclid` traits from `dashu-base`; `DivRem` returns both quotient and remainder at once. @@ -14,15 +14,15 @@ let e = 2 * &b - 1; // mixes naturally with primitives assert_eq!(e, IBig::from(-0x21ff)); ``` -## Float Arithmetic +### Float Arithmetic `FBig`/`DBig` support `+`, `-`, `*`, `/` between values of the **same base and rounding mode** (mixed bases are a compile error by design). The result precision is `max(lhs.precision, rhs.precision)`, and each operation reports its inexactness through the two-layer API described in [Exponential and Logarithm](./exp_log.md). Infinities are terminal: `1/0` and `ln(0)` produce `±∞`, but feeding an infinity back into arithmetic is an error (`FpError::InfiniteInput`). -## Rational Arithmetic +### Rational Arithmetic `RBig` supports `+`, `-`, `*`, `/`. Division by zero panics. `Relaxed` performs the same operations without auto-reducing to lowest terms (faster for a chain of operations); call `canonicalize()` to reduce when needed. -## Complex Arithmetic +### Complex Arithmetic `CBig` supports the field operations `+`, `-`, `*`, `/`, plus `sqr` and `inv` (multiplicative inverse). Multiplication and division by a real `FBig` are also available as mixed-type operators. Multiplication and division use Smith's method with a guard digit and re-round, giving the same near-correctly-rounded guarantee as `dashu-float`'s transcendentals. @@ -36,6 +36,6 @@ let sum = &z + &C::I; // (3+4i) + i = 3+5i assert_eq!(sum.im().significand(), &5.into()); ``` -## Mixed-type arithmetic +### Mixed-type arithmetic There are **no implicit mixed-type operators** between different big-number kinds (e.g. `UBig + FBig` does not compile) — convert explicitly first (see [Conversion](../convert.md)). diff --git a/guide/src/ops/bit.md b/guide/src/ops/bit.md index e9a89088..3295c7eb 100644 --- a/guide/src/ops/bit.md +++ b/guide/src/ops/bit.md @@ -1,4 +1,4 @@ -# Bit Manipulation +## Bit Manipulation `UBig` and `IBig` support the bitwise operators `&` (and), `|` (or), `^` (xor), and `!` (not). On `UBig`, `!` is an *infinite-width* complement — every bit above the highest set bit is treated as `1`, so `!n` is generally a very large number. On `IBig`, `!` follows the two's-complement rule. @@ -11,15 +11,15 @@ assert_eq!(format!("{:b}", &a & &b), "1000"); assert_eq!(format!("{:b}", &a | &b), "1110"); ``` -## Bit testing and length +### Bit testing and length The `BitTest` trait (from `dashu-base`) tests and measures individual bits: `.bit(n)` returns the `n`-th bit, and `.bit_len()` returns the position of the highest set bit plus one. `set_bit(n)` / `clear_bit(n)` mutate a `UBig` in place, and `trailing_zeros()` counts the low-order zero bits. -## Shifts +### Shifts `<<` and `>>` shift by a `usize`. Left shifts grow the number; right shifts shrink it and are equivalent to floor-division by a power of two. -## Using `UBig` as a bit vector +### Using `UBig` as a bit vector Because a `UBig` has unbounded width, it works naturally as an arbitrarily large bit set: set bit `i` with `set_bit(i)`, test it with `bit(i)`, and read the extent with `bit_len()`. diff --git a/guide/src/ops/cmp.md b/guide/src/ops/cmp.md index 170ad7c5..4e664408 100644 --- a/guide/src/ops/cmp.md +++ b/guide/src/ops/cmp.md @@ -1,19 +1,19 @@ -# Equality and Comparison +## Equality and Comparison Comparison is natively enabled **only between big numbers of the same kind**, not between big numbers and primitive types — this avoids the trait-overlap problem described in [`num-bigint`#150](https://github.com/rust-num/num-bigint/issues/150). To compare a big number with a primitive type, enable the `num-order` feature and use the `NumOrd` trait. -## Equality +### Equality `PartialEq`/`Eq` is value equality. For `FBig`/`DBig` it compares the representation and ignores the context (precision and rounding mode), so two floats with different precision but the same value compare equal. Signed zeros compare equal: `+0 == -0`. `CBig` compares componentwise, with `+0 == -0` on each part. -## Ordering +### Ordering `UBig`/`IBig`/`RBig`/`FBig`/`DBig` carry the natural numeric total order (`Ord`). Infinities are placed at the ends: $-\infty < \text{finite} < +\infty$. `CBig` defines a lexicographic total order by `(re, then im)` — usable for sorting and `BTreeMap`, but note it is *not* an algebraic magnitude ordering. -## Sign +### Sign The signed types (`IBig`, `FBig`/`DBig`, `RBig`, `CBig`) expose `.sign()` (returning `dashu_base::Sign`, where zero is `Positive`) and `.signum()` (returning `-1`, `0`, or `+1` as the same type). -## Magnitude comparison and cross-type ordering +### Magnitude comparison and cross-type ordering `AbsOrd` (from `dashu-base`) compares by absolute value; for `CBig` it compares by $|z|$. The `num-order` feature adds `NumOrd` for ordering and `NumHash` for hashing across different numeric types (big and primitive), keeping them consistent with each other. diff --git a/guide/src/ops/exp_log.md b/guide/src/ops/exp_log.md index 96469c12..83620887 100644 --- a/guide/src/ops/exp_log.md +++ b/guide/src/ops/exp_log.md @@ -1,15 +1,15 @@ -# Exponential and Logarithm +## Exponential and Logarithm `FBig`/`DBig` provide the exponential, logarithmic, power, and root families, plus the mathematical constants. `CBig` provides the complex analogs of each. -## Two-layer API +### Two-layer API Like all inexact operations, transcendentals come in two layers (see [types](../types.md)): - **Context layer** — `Context` methods take a `&Repr` and return `FpResult>` (a correctly-rounded result or an `FpError`), carrying the rounding direction. They accept an optional `&mut ConstCache` for constant reuse. - **Convenience layer** — methods on `FBig` (`.exp()`, `.ln()`, …) unwrap to a plain `FBig`, panicking on `Indeterminate`/`OutOfDomain`/`InfiniteInput` and saturating overflow/underflow to `±∞`/`±0`. -## Real functions +### Real functions - Exponential: `exp`, `exp_m1` ($e^x - 1$, accurate near zero). - Logarithm: `ln`, `ln_1p` ($\ln(1+x)$, accurate near zero). @@ -18,7 +18,7 @@ Like all inexact operations, transcendentals come in two layers (see [types](../ (`exp2`/`exp10`/`log2`/`log10` are deferred to a later 0.5.x release.) -## Complex functions +### Complex functions `CBig` mirrors the real set with `exp`, `ln`, `sqrt`, `powi`, and `powf`, built on the real implementations. The identities are diff --git a/guide/src/ops/index.md b/guide/src/ops/index.md index d909e6a7..d9d18081 100644 --- a/guide/src/ops/index.md +++ b/guide/src/ops/index.md @@ -1,4 +1,4 @@ -# Operations +## Operations dashu implements a full set of arithmetic, comparison, bitwise, and number-theoretic operations for its numeric types, following standard Rust operator conventions. This section covers each category: diff --git a/guide/src/ops/num_theory.md b/guide/src/ops/num_theory.md index 9dcb4731..e3a6acd9 100644 --- a/guide/src/ops/num_theory.md +++ b/guide/src/ops/num_theory.md @@ -1,8 +1,8 @@ -# Number Theoretic +## Number Theoretic `dashu-int` provides greatest-common-divisor and modular-arithmetic primitives. -## Greatest common divisor +### Greatest common divisor The `Gcd` trait (from `dashu-base`) gives `gcd`, and `ExtendedGcd` gives `gcd_ext`, which returns `(gcd, x, y)` with $a\cdot x + b\cdot y = \gcd(a,b)$. @@ -15,7 +15,7 @@ let b = UBig::from(8u8); assert_eq!((&a).gcd(&b), UBig::from(4u8)); ``` -## Modular arithmetic +### Modular arithmetic For repeated operations against a fixed modulus, precompute a `ConstDivisor` and reduce values into `Reduced`. Addition, subtraction, multiplication, exponentiation, and inversion then run against the precomputed modulus, and the result prints in `(mod N)` form. @@ -28,6 +28,6 @@ let y = ring.reduce(55443); assert_eq!(format!("{}", x - y), "6902 (mod 10000)"); ``` -## Diophantine approximation +### Diophantine approximation Rational approximation of reals — the simplest rational within a tolerance, continued fractions — lives on `RBig`; see [Conversion](../convert.md#conversion-to-rbig) for `simplest_in` / `nearest_in`. diff --git a/guide/src/ops/trig_n_hyper.md b/guide/src/ops/trig_n_hyper.md index 43c22bc2..5008fd4d 100644 --- a/guide/src/ops/trig_n_hyper.md +++ b/guide/src/ops/trig_n_hyper.md @@ -1,15 +1,15 @@ -# Trigonometric and Hyperbolic Functions +## Trigonometric and Hyperbolic Functions `FBig`/`DBig` and `CBig` provide the trigonometric and hyperbolic functions. They are grouped on one page because the complex circular functions are built from the real circular *and* hyperbolic functions. -## Real functions +### Real functions - Circular: `sin`, `cos`, `tan`, and `sin_cos` (both at once); inverses `asin`, `acos`, `atan`, and the four-quadrant `atan2(y, x)`. - Hyperbolic: `sinh`, `cosh`, `tanh`, `sinh_cosh`; inverses `asinh`, `acosh`, `atanh`. Angles are in radians. `atan2` follows the C99 signed-zero model, which matters for correct branch-cut behavior on the axes. -## Complex functions +### Complex functions `CBig` provides the circular family `sin`, `cos`, `tan`, `sin_cos`, `asin`, `acos`, and `atan`. They are evaluated from the real `sin`/`cos` and `sinh`/`cosh` via the identities diff --git a/guide/src/performance.md b/guide/src/performance.md index 86780337..b468f1ad 100644 --- a/guide/src/performance.md +++ b/guide/src/performance.md @@ -1,4 +1,4 @@ -# Performance +## Performance `dashu` aims to be efficient while staying portable. By default it compiles for the generic baseline of each target architecture, so a binary that depends on @@ -7,7 +7,7 @@ the generic baseline of each target architecture, so a binary that depends on When big-number arithmetic is on the hot path, you can get a meaningful speedup by telling the compiler which CPU you are actually running on. -## Build with `target-cpu=native` +### Build with `target-cpu=native` The single most impactful setting is to compile with the host CPU's feature set: @@ -43,7 +43,7 @@ Note that `target-cpu=native` targets the CPU of the **machine doing the build**. If you build on one host and deploy to another, prefer an explicit `target-cpu`/`target-feature` that matches the deployment hardware instead. -## Runtime feature detection (default builds) +### Runtime feature detection (default builds) Even in a default baseline build, `dashu-int`'s hottest basecase multiplication kernels dispatch at runtime to a BMI2 (`mulx`) implementation on x86-64 when diff --git a/guide/src/types.md b/guide/src/types.md index 26978e64..2d01accf 100644 --- a/guide/src/types.md +++ b/guide/src/types.md @@ -1,4 +1,4 @@ -# Numeric Types +## Numeric Types In `dashu` crates, there are standalone types for each kind of numbers with arbitrary precision, as listed below: @@ -11,39 +11,39 @@ In `dashu` crates, there are standalone types for each kind of numbers with arbi Common operations are implemented for all these numeric types, please refer to the other sections or the API docs for the usages. -## Word +### Word A `dashu_int::Word` is an unsigned integer representing a native machine word. The size of a `Word` usually depends on the platform, for example, the `Word` is `u32` on 32-bit platforms. However, the behavior can be overriden by setting the `force_bits` config flag (e.g. add `--cfg force_bits="32"` to the environment variable `RUSTFLAGS`). Since this type is not consistant across platforms, be careful to use it when writing portable programs. Moreover, there is another type `DoubleWord` representing an integer type with double the size of a `Word`. It's the maximum integer type that can fit in a `UBig` instance without heap allocation. It's also involved in some const constructors. -## Sign +### Sign A `dashu_base::Sign` is a **binary** enum to represent the sign of numbers. Due to effciency and clarity, the number zero will be categorized as `Sign::Positive`, even though it's mathematically unsigned. (Imagine if you store the sign in a ternary format, every number instance will have to pay an extra bit to store the sign, and extra branches to do operations.) To get a ternary representation, it's recommended to use the `.signum()` methods on the numeric types. Convenient utilities related to the sign are provided with this enum. For example, you can get the sign of any primitive numbers or big numbers through the `dashu_base::Signed` trait, you can also multiply the sign by another sign. You can even multiply the sign with `core::cmp::Ordering`, this is very handy when you want to flip a comparison result based on the sign of operands, and this is widely used in the comparison implementations in `dashu`. -## Layout of `UBig` +### Layout of `UBig` The most fundamental type of the `dashu` libraries is the natural number `UBig`. The underlying representation of an `UBig` number is an array of `Word`s. What's special about `dashu` is that when it contains only one or two words, the words will be inlined and no heap allocation will happen. Furthermore, an `UBig` usually only occupies a stack space of 3 words when it's inlined (see the code for the details if you are interested). Thanks to special memory optimization in `dashu`, an `Option` and even a `Option` will also take only 3 words. > Currently the memory layout of an `UBig` instance is not finalized, so don't rely on this by now. Besides, there will be no compatiblity guarantee for the memory layout between different versions. The memory layout will probably be stablized in a `v1.0.0` release. -## Layout of `FBig` +### Layout of `FBig` The layout of `FBig` (and `DBig`) is a little different from other types. An `FBig` instance contains a number representation `dashu_float::Repr` and a context `dashu_float::Context`. The context will be copied every time a new `FBig` is created based on it. The context currently contains the rounding information and the precision associated with this number. The context is kept deliberately lightweight (`Copy` + `Send` + `Sync`): the shared cache for math constants (such as π, ln2, ln10) lives *outside* the context, in the separate [`CachedFBig`](./construct.md#cached-arithmetic-for-fbig) wrapper, so that a plain `FBig` stays cheap to copy and usable in `const`/`static` contexts. Therefore, if you don't want to store the additional context information, you can just store the `Repr` part of the `FBig`. The later operations on the `Repr` can be called with the associated methods of the `Context`, which all takes the reference to a `Repr` instance. However, this could lead to a little overhead in some cases. -## Layout of `CBig` +### Layout of `CBig` `CBig` (in the `dashu-cmplx` crate) mirrors `FBig`'s `Repr`+`Context` layout, generalized to two parts: a `CBig` instance holds two `Repr` parts — the real part `re` and the imaginary part `im` — over a **single shared** `Context`. Storing one context (rather than wrapping two `FBig`s, each carrying its own) makes the uniform-precision invariant *physical*: there is exactly one precision slot, so `re` and `im` structurally cannot disagree. Each part keeps its own significand length; the shared context holds only the precision cap and the single rounding mode, applied independently to each component. As with `FBig`, the context is `Copy` while the significands are heap-allocated, so `CBig` is `Clone` but not `Copy`. Because both parts are `Repr`, `CBig` reuses `dashu-float`'s signed-zero / signed-infinity / branch-cut machinery unchanged. It follows the C99 Annex G / Kahan model (see [Standards Compliance](./compliance.md)) and, like `FBig`, has **no NaN** — C99 cases that would produce a complex NaN are reported as `FpError` at the context layer. `CBig::from_parts(re, im)` takes the larger of the two operand contexts. Construction, arithmetic, transcendentals, and I/O are covered in [Construction and Destruction](./construct.md), [Conversion](./convert.md), and [Operations](./ops/index.md). -# Auxiliary Types +## Auxiliary Types Besides the numeric types, there are several auxiliary types used across the crates: `Sign` and `Approximation` in **dashu-base**, `ConstCache` and `FpResult` in **dashu-float**, and `CfpResult` in **dashu-cmplx**. -## Sign +### Sign In `dashu`, the sign of the numbers are represented as an enum `dashu_base::Sign`. It only has two variants: `Positive` and `Negative`. Zero is considered as `Positive`. A `Sign` can be converted from a boolean value using `::from()`, where `true` is mapped to `Negative`. @@ -51,16 +51,16 @@ To get the sign of a number, usually there is a `.sign()` method for the numeric The type `Sign` also supports some operations, namely `Neg` and `Mul`. The sign can be flipped using `Neg` and it can be multiplied with another `Sign` or other numeric types to their signs. -## Approximation +### Approximation The enum `Approximation` is another commonly used type in `dashu`. It's used when an operation can return inexact values (such as rounding and number conversion). The enum has two variants: `Exact` and `Inexact`, the latter one contains a error term for representing the sign or magnitude of the error caused by inexact operations. When you have an `Approximation` instance, call `.value()`, `.value_ref()` or `unwrap()` to get the operation result, and call `.error()` to get the error term. This struct also support method to work in functional programming style, such as `.map()` and `.and_then()`. -## ConstCache +### ConstCache `dashu_float::ConstCache` holds the exact binary-splitting state for the mathematical constants π, ln2, and ln10, so repeated transcendental calls at increasing precision *extend* prior work instead of recomputing from scratch. It is a plain struct of big integers — base-free, `Send` + `Sync` — and a single cache serves any base. `FBig` and `Context` themselves stay `Copy` and carry no cache; the state lives in the separate [`CachedFBig`](./construct.md#cached-arithmetic-for-fbig) wrapper (as `Rc>`), or you can drive a bare `ConstCache` directly. -## FpResult and CfpResult +### FpResult and CfpResult Inexact operations at the context layer return a result type rather than a bare value: `dashu_float::FpResult = Result, FpError>`, where `Rounded` is the [`Approximation`](#approximation) carrying a `Rounding` flag. The complex analog is `dashu_cmplx::CfpResult` (`Result, FpError>`), whose `CRounded` carries one `Rounding` flag per axis. `FpError` reports why an operation could not produce a finite correctly-rounded value: `Overflow`/`Underflow` (saturated to `±∞`/`±0` by the convenience layer), `Indeterminate` (e.g. `0/0`), `OutOfDomain`, and `InfiniteInput`. The convenience-layer methods unwrap these — saturating overflow/underflow and panicking on the rest. From b290a1601226f207bcbba9c0e6d26ef753173b3d Mon Sep 17 00:00:00 2001 From: Jacob Zhong Date: Mon, 6 Jul 2026 11:03:25 +0800 Subject: [PATCH 09/21] Add CBig::NEG_ONE; document it in the guide The guide's Constants section listed ::NEG_ONE for the other signed types but CBig didn't define it, so add the constant for real: - complex/src/cbig.rs: add `CBig::NEG_ONE` (`-1 + 0i`), mirroring FBig::NEG_ONE; extend the `constants` unit test (non-zero, distinct from ONE). - guide/src/construct.md: list `::NEG_ONE` on the CBig constants line. - complex/CHANGELOG.md: Unreleased ### Add entry. Co-Authored-By: Claude --- complex/CHANGELOG.md | 3 +++ complex/src/cbig.rs | 5 +++++ guide/src/construct.md | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/complex/CHANGELOG.md b/complex/CHANGELOG.md index 25e2f140..be88b374 100644 --- a/complex/CHANGELOG.md +++ b/complex/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +### Add +- `CBig::NEG_ONE` (`-1 + 0i`), mirroring `FBig::NEG_ONE`. + ### Improve - The complex `sin_cos` kernel now calls `dashu-float`'s combined `sinh_cosh` (new in `dashu-float`) instead of separate `sinh` + `cosh` calls, sharing the `exp_m1(±y)` sub-computations. diff --git a/complex/src/cbig.rs b/complex/src/cbig.rs index 004a17de..d2f0784d 100644 --- a/complex/src/cbig.rs +++ b/complex/src/cbig.rs @@ -95,6 +95,9 @@ impl CBig { /// The complex number one `1 + 0i` (unlimited precision). pub const ONE: Self = Self::new(Repr::one(), Repr::zero(), Context::new(0)); + /// The complex number negative one `-1 + 0i` (unlimited precision). + pub const NEG_ONE: Self = Self::new(Repr::neg_one(), Repr::zero(), Context::new(0)); + /// The imaginary unit `0 + 1i` (unlimited precision). pub const I: Self = Self::new(Repr::zero(), Repr::one(), Context::new(0)); @@ -196,6 +199,8 @@ mod tests { fn constants() { assert!(C::ZERO.is_zero()); assert!(!C::ONE.is_zero()); + assert!(!C::NEG_ONE.is_zero()); + assert!(C::NEG_ONE != C::ONE); assert!(!C::I.is_zero()); let (re, im) = C::I.into_parts(); assert!(re.repr().is_zero()); diff --git a/guide/src/construct.md b/guide/src/construct.md index 9749f1d3..807684c2 100644 --- a/guide/src/construct.md +++ b/guide/src/construct.md @@ -8,7 +8,7 @@ For all the numeric types, there are several constants associated with the type. - `IBig`: `::ZERO`, `::ONE`, `::NEG_ONE` - `FBig`/`DBig`: `::ZERO`, `::ONE`, `::NEG_ONE`, `::INFINITY`, `::NEG_INFINITY` - `RBig`: `::ZERO`, `::ONE`, `::NEG_ONE` -- `CBig`: `::ZERO` ($0+0i$), `::ONE` ($1+0i$), `::I` ($0+1i$) +- `CBig`: `::ZERO` ($0+0i$), `::ONE` ($1+0i$), `::NEG_ONE` ($-1+0i$), `::I` ($0+1i$) ## Raw Constructor for `UBig` From 8202e31933912a249d41b40ffaf14fd0700d0e0c Mon Sep 17 00:00:00 2001 From: Jacob Zhong Date: Mon, 6 Jul 2026 12:28:03 +0800 Subject: [PATCH 10/21] Guide: split Cached Arithmetic into its own chapter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move "Cached Arithmetic for FBig" out of construct.md into a new top-level chapter cached.md, as a sibling of Construction and Destruction. Repoint the CachedFBig cross-references in types.md (×2) and ops/exp_log.md to the new page; construct.md now covers only construction/destruction. Co-Authored-By: Claude --- guide/src/SUMMARY.md | 1 + guide/src/cached.md | 148 ++++++++++++++++++++++++++++++++++++++ guide/src/construct.md | 151 +-------------------------------------- guide/src/ops/exp_log.md | 2 +- guide/src/types.md | 4 +- 5 files changed, 153 insertions(+), 153 deletions(-) create mode 100644 guide/src/cached.md diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md index c9c1539d..8712a67c 100644 --- a/guide/src/SUMMARY.md +++ b/guide/src/SUMMARY.md @@ -4,6 +4,7 @@ - [Types](./types.md) - [Construction and Destruction](./construct.md) +- [Cached Arithmetic for FBig](./cached.md) - [Conversion](./convert.md) - [Input and Output](./io/index.md) - [Parsing](./io/parse.md) diff --git a/guide/src/cached.md b/guide/src/cached.md new file mode 100644 index 00000000..5859ca17 --- /dev/null +++ b/guide/src/cached.md @@ -0,0 +1,148 @@ +## Cached Arithmetic for FBig + +The [`CachedFBig`] type is an [`FBig`] that carries a shared handle to a +`Rc>`. The cache stores exact binary-splitting state for +mathematical constants (π, ln2, ln10), so that transcendental operations +(`ln`, `exp`, `sin`, `cos`, …, `pi`) reuse and progressively extend prior +work instead of recomputing from scratch. + +### Creation + +A `CachedFBig` is created by attaching a cache handle to an `FBig`: + +```rust +use std::rc::Rc; +use core::cell::RefCell; +use dashu_float::{CachedFBig, ConstCache, FBig, Repr, Context}; + +let cache = Rc::new(RefCell::new(ConstCache::new())); + +// From an FBig +let a = FBig::ONE.into_cached(cache.clone()); + +// From raw parts with a fresh cache +let b = CachedFBig::<_, 10>::with_cache( + Repr::new(1234.into(), -3), + Context::new(50), +); +``` + +Use `From for CachedFBig` for one-off conversions (it creates a fresh +empty cache): + +```rust +let c: CachedFBig = FBig::from(3u8).into(); +``` + +To drop the cache and get back a plain `FBig`, use `into_fbig()` or the +`From for FBig` trait: + +```rust +let plain: FBig = cached.into(); // or cached.into_fbig() +``` + +### Cache sharing + +Binary operations between `CachedFBig` values preserve the cache handle in +the result: `(a + b).ln().exp()` keeps extending the same cache throughout. +When two operands carry different cache handles, the **left-hand side** cache +is preserved. For `FBig op CachedFBig`, the `CachedFBig` operand's cache is +preserved regardless of which side it is on. + +Operations with plain `FBig` and primitives (`u8`, `i32`, `UBig`, etc.) also +work and preserve the `CachedFBig` operand's cache: + +```rust +let cached = CachedFBig::<_, 10>::with_cache( + Repr::new(2.into(), 0), Context::new(20), +); +let result = cached + 3u8; // CachedFBig, cache preserved +let result = 10i32 * cached; // CachedFBig, cache preserved +``` + +### Inspecting and clearing the cache + +Use `cache()` to borrow the cache read-only and inspect its size: + +```rust +let terms = cached.cache().total_terms(); +let words = cached.cache().total_words(); +``` + +Call `clear_cache()` to free all cached big-integer memory. The next +transcendental operation will recompute constants from scratch: + +```rust +cached.clear_cache(); +assert_eq!(cached.cache().total_terms(), 0); +``` + +### More constructors and accessors + +Beyond `into_cached` / `with_cache` / `From`, `CachedFBig` mirrors the rest of `FBig`'s construction surface while preserving the cache handle: + +- `from_parts(significand, exponent)` — build from a significand and exponent, with a fresh cache. +- `with_rounding::()` — change the rounding mode, keeping the cache handle. +- `as_fbig()` — borrow the inner `FBig` immutably (cheap; no cache detach). +- `from_repr(repr, context, cache)` / `into_repr()` — the raw-repr constructor/destructor that share a specific cache handle. + +### Computing constants directly + +The cache stores exact binary-splitting state for the constants π, ln2, and ln10, so the methods that produce them reuse and progressively extend prior work rather than recomputing from scratch. On `CachedFBig`, π is a single call: + +```rust +use std::rc::Rc; +use core::cell::RefCell; +use dashu_float::{CachedFBig, ConstCache}; +use dashu_float::round::mode::HalfAway; + +let cache = Rc::new(RefCell::new(ConstCache::new())); +let _pi = CachedFBig::::pi(100, &cache); +// a later, higher-precision call extends the same cached state instead of restarting +let _pi_more = CachedFBig::::pi(1000, &cache); +``` + +You can also drive a bare `ConstCache` directly, without a `CachedFBig` — useful when you want the constants but not the per-value wrapper. The methods are generic over base and rounding mode, and a single cache serves any base: + +```rust +use dashu_float::ConstCache; +use dashu_float::round::mode::HalfAway; + +let mut cache = ConstCache::new(); +let pi = cache.pi::<10, HalfAway>(100).value(); // computes from scratch +let pi_1000 = cache.pi::<10, HalfAway>(1000).value(); // extends the cached state +let ln2 = cache.ln2::<10, HalfAway>(100); +let ln10 = cache.ln10::<10, HalfAway>(100); +``` + +`ln_base::(precision)` dispatches to the cached ln2 / ln10 when `B` is 2 or 10 (or a power of two), and falls back to a direct `ln(B)` otherwise. + +### Thread safety + +`CachedFBig` carries its cache as `Rc>`, so it is **`!Send + !Sync`** — a cached value cannot move across threads. `FBig` itself stays `Copy + Send + Sync` (which is why `static_fbig!` keeps working); only the cached wrapper is non-thread-safe. `ConstCache` is a plain struct of big integers and is itself `Send + Sync`, so to share one cache across threads, wrap a `ConstCache` (or a `CachedFBig`) in `Arc>`. The underlying `Context` methods accept `Option<&mut ConstCache>` regardless of the container, so this needs no API change. + +### Worked example: reusing constants across a chain + +Because every value-producing operation preserves the cache handle, a chain of transcendentals reuses the same constants throughout. Building several results from one shared handle pays for each constant once: + +```rust +use std::rc::Rc; +use core::cell::RefCell; +use dashu_float::{CachedFBig, ConstCache, Context, Repr}; +use dashu_float::round::mode::HalfAway; + +type F = CachedFBig; +let cache = Rc::new(RefCell::new(ConstCache::new())); + +// π is computed into the shared cache... +let _pi_50 = F::pi(50, &cache); +// ...and a later, higher-precision call extends it instead of restarting +let _pi_1000 = F::pi(1000, &cache); + +// an arithmetic chain built on the same handle keeps it end to end +let a = F::from_repr(Repr::new(2.into(), 0), Context::new(50), cache.clone()); +let b = F::from_repr(Repr::new(3.into(), 0), Context::new(50), cache.clone()); +let _ = (a + b).ln().exp(); + +assert!(cache.borrow().total_terms() > 0); +``` diff --git a/guide/src/construct.md b/guide/src/construct.md index 807684c2..72b88eba 100644 --- a/guide/src/construct.md +++ b/guide/src/construct.md @@ -40,157 +40,8 @@ To deconstruct these numeric types, use the `::into_parts()` functions to get th We also provide a convenient and efficient way to create large numbers from literals through the macros `ubig!`/`ibig!`/`fbig!`/`dbig!`/`rbig!`/`cbig!`. These macros can be obtained directly from the `dashu-macros` crate or from the `dashu` meta crate. The `cbig!` macro accepts the same algebraic form as `CBig`'s `FromStr` (e.g. `cbig!(3+4i)`, `cbig!(-i)`) or a `re, im` pair (e.g. `cbig!(3, 4)`). -You can directly put numeric literals as the argument without quotes (e.g. `dbig!(3.1415926535897932384626)`), and you don't need to worry about precision loss, because it's guaranteed that the number is faithfully created without approximations. Besides, the macros have minimal runtime overhead, since the numbers are preprocessed by the macros during compile-time. +You can directly put numeric literals as the argument without quotes (e.g. `dbig!(3.1415926535897932384626)`), and you don't need to worry about precision loss, because it's guaranteed that the number is faithfully created without approximations. Besides, the macros have minimal runtime overhead, since the numbers are preprocessed by the macros during compile-time. When the number doesn't have a high precision, these macros can be used in a `const` environment, however this ability dependends on the precision and the machine word size. To create large constants, you can use the `static_*` macros (such as `static_ubig!`) in the crate. They have the same syntax as the normal macros, but the different is that the outputs of the macros are references to a static instance, rather than directly generating an instance. There are also other limitations about these macros for static creation. Please refer to [the docs of `dashu-macros`](https://docs.rs/dashu-macros/latest/dashu_macros/) for detailed usage of these macros. - -## Cached Arithmetic for FBig - -The [`CachedFBig`] type is an [`FBig`] that carries a shared handle to a -`Rc>`. The cache stores exact binary-splitting state for -mathematical constants (π, ln2, ln10), so that transcendental operations -(`ln`, `exp`, `sin`, `cos`, …, `pi`) reuse and progressively extend prior -work instead of recomputing from scratch. - -### Creation - -A `CachedFBig` is created by attaching a cache handle to an `FBig`: - -```rust -use std::rc::Rc; -use core::cell::RefCell; -use dashu_float::{CachedFBig, ConstCache, FBig, Repr, Context}; - -let cache = Rc::new(RefCell::new(ConstCache::new())); - -// From an FBig -let a = FBig::ONE.into_cached(cache.clone()); - -// From raw parts with a fresh cache -let b = CachedFBig::<_, 10>::with_cache( - Repr::new(1234.into(), -3), - Context::new(50), -); -``` - -Use `From for CachedFBig` for one-off conversions (it creates a fresh -empty cache): - -```rust -let c: CachedFBig = FBig::from(3u8).into(); -``` - -To drop the cache and get back a plain `FBig`, use `into_fbig()` or the -`From for FBig` trait: - -```rust -let plain: FBig = cached.into(); // or cached.into_fbig() -``` - -### Cache sharing - -Binary operations between `CachedFBig` values preserve the cache handle in -the result: `(a + b).ln().exp()` keeps extending the same cache throughout. -When two operands carry different cache handles, the **left-hand side** cache -is preserved. For `FBig op CachedFBig`, the `CachedFBig` operand's cache is -preserved regardless of which side it is on. - -Operations with plain `FBig` and primitives (`u8`, `i32`, `UBig`, etc.) also -work and preserve the `CachedFBig` operand's cache: - -```rust -let cached = CachedFBig::<_, 10>::with_cache( - Repr::new(2.into(), 0), Context::new(20), -); -let result = cached + 3u8; // CachedFBig, cache preserved -let result = 10i32 * cached; // CachedFBig, cache preserved -``` - -### Inspecting and clearing the cache - -Use `cache()` to borrow the cache read-only and inspect its size: - -```rust -let terms = cached.cache().total_terms(); -let words = cached.cache().total_words(); -``` - -Call `clear_cache()` to free all cached big-integer memory. The next -transcendental operation will recompute constants from scratch: - -```rust -cached.clear_cache(); -assert_eq!(cached.cache().total_terms(), 0); -``` - -### More constructors and accessors - -Beyond `into_cached` / `with_cache` / `From`, `CachedFBig` mirrors the rest of `FBig`'s construction surface while preserving the cache handle: - -- `from_parts(significand, exponent)` — build from a significand and exponent, with a fresh cache. -- `with_rounding::()` — change the rounding mode, keeping the cache handle. -- `as_fbig()` — borrow the inner `FBig` immutably (cheap; no cache detach). -- `from_repr(repr, context, cache)` / `into_repr()` — the raw-repr constructor/destructor that share a specific cache handle. - -### Computing constants directly - -The cache stores exact binary-splitting state for the constants π, ln2, and ln10, so the methods that produce them reuse and progressively extend prior work rather than recomputing from scratch. On `CachedFBig`, π is a single call: - -```rust -use std::rc::Rc; -use core::cell::RefCell; -use dashu_float::{CachedFBig, ConstCache}; -use dashu_float::round::mode::HalfAway; - -let cache = Rc::new(RefCell::new(ConstCache::new())); -let _pi = CachedFBig::::pi(100, &cache); -// a later, higher-precision call extends the same cached state instead of restarting -let _pi_more = CachedFBig::::pi(1000, &cache); -``` - -You can also drive a bare `ConstCache` directly, without a `CachedFBig` — useful when you want the constants but not the per-value wrapper. The methods are generic over base and rounding mode, and a single cache serves any base: - -```rust -use dashu_float::ConstCache; -use dashu_float::round::mode::HalfAway; - -let mut cache = ConstCache::new(); -let pi = cache.pi::<10, HalfAway>(100).value(); // computes from scratch -let pi_1000 = cache.pi::<10, HalfAway>(1000).value(); // extends the cached state -let ln2 = cache.ln2::<10, HalfAway>(100); -let ln10 = cache.ln10::<10, HalfAway>(100); -``` - -`ln_base::(precision)` dispatches to the cached ln2 / ln10 when `B` is 2 or 10 (or a power of two), and falls back to a direct `ln(B)` otherwise. - -### Thread safety - -`CachedFBig` carries its cache as `Rc>`, so it is **`!Send + !Sync`** — a cached value cannot move across threads. `FBig` itself stays `Copy + Send + Sync` (which is why `static_fbig!` keeps working); only the cached wrapper is non-thread-safe. `ConstCache` is a plain struct of big integers and is itself `Send + Sync`, so to share one cache across threads, wrap a `ConstCache` (or a `CachedFBig`) in `Arc>`. The underlying `Context` methods accept `Option<&mut ConstCache>` regardless of the container, so this needs no API change. - -### Worked example: reusing constants across a chain - -Because every value-producing operation preserves the cache handle, a chain of transcendentals reuses the same constants throughout. Building several results from one shared handle pays for each constant once: - -```rust -use std::rc::Rc; -use core::cell::RefCell; -use dashu_float::{CachedFBig, ConstCache, Context, Repr}; -use dashu_float::round::mode::HalfAway; - -type F = CachedFBig; -let cache = Rc::new(RefCell::new(ConstCache::new())); - -// π is computed into the shared cache... -let _pi_50 = F::pi(50, &cache); -// ...and a later, higher-precision call extends it instead of restarting -let _pi_1000 = F::pi(1000, &cache); - -// an arithmetic chain built on the same handle keeps it end to end -let a = F::from_repr(Repr::new(2.into(), 0), Context::new(50), cache.clone()); -let b = F::from_repr(Repr::new(3.into(), 0), Context::new(50), cache.clone()); -let _ = (a + b).ln().exp(); - -assert!(cache.borrow().total_terms() > 0); -``` diff --git a/guide/src/ops/exp_log.md b/guide/src/ops/exp_log.md index 83620887..3e7901da 100644 --- a/guide/src/ops/exp_log.md +++ b/guide/src/ops/exp_log.md @@ -14,7 +14,7 @@ Like all inexact operations, transcendentals come in two layers (see [types](../ - Exponential: `exp`, `exp_m1` ($e^x - 1$, accurate near zero). - Logarithm: `ln`, `ln_1p` ($\ln(1+x)$, accurate near zero). - Powers and roots: `powi(IBig)`, `powf(&FBig)`, `sqrt`, `cbrt`, `nth_root(&n)`, and `hypot(&other)` ($\sqrt{x^2+y^2}$, overflow-safe). -- Constants: `FBig::pi(precision)` computes π; use [`CachedFBig`](../construct.md#cached-arithmetic-for-fbig) to reuse it across calls. +- Constants: `FBig::pi(precision)` computes π; use [`CachedFBig`](../cached.md) to reuse it across calls. (`exp2`/`exp10`/`log2`/`log10` are deferred to a later 0.5.x release.) diff --git a/guide/src/types.md b/guide/src/types.md index 2d01accf..a5b96f60 100644 --- a/guide/src/types.md +++ b/guide/src/types.md @@ -31,7 +31,7 @@ The most fundamental type of the `dashu` libraries is the natural number `UBig`. ### Layout of `FBig` -The layout of `FBig` (and `DBig`) is a little different from other types. An `FBig` instance contains a number representation `dashu_float::Repr` and a context `dashu_float::Context`. The context will be copied every time a new `FBig` is created based on it. The context currently contains the rounding information and the precision associated with this number. The context is kept deliberately lightweight (`Copy` + `Send` + `Sync`): the shared cache for math constants (such as π, ln2, ln10) lives *outside* the context, in the separate [`CachedFBig`](./construct.md#cached-arithmetic-for-fbig) wrapper, so that a plain `FBig` stays cheap to copy and usable in `const`/`static` contexts. Therefore, if you don't want to store the additional context information, you can just store the `Repr` part of the `FBig`. The later operations on the `Repr` can be called with the associated methods of the `Context`, which all takes the reference to a `Repr` instance. However, this could lead to a little overhead in some cases. +The layout of `FBig` (and `DBig`) is a little different from other types. An `FBig` instance contains a number representation `dashu_float::Repr` and a context `dashu_float::Context`. The context will be copied every time a new `FBig` is created based on it. The context currently contains the rounding information and the precision associated with this number. The context is kept deliberately lightweight (`Copy` + `Send` + `Sync`): the shared cache for math constants (such as π, ln2, ln10) lives *outside* the context, in the separate [`CachedFBig`](./cached.md) wrapper, so that a plain `FBig` stays cheap to copy and usable in `const`/`static` contexts. Therefore, if you don't want to store the additional context information, you can just store the `Repr` part of the `FBig`. The later operations on the `Repr` can be called with the associated methods of the `Context`, which all takes the reference to a `Repr` instance. However, this could lead to a little overhead in some cases. ### Layout of `CBig` @@ -59,7 +59,7 @@ When you have an `Approximation` instance, call `.value()`, `.value_ref()` or `u ### ConstCache -`dashu_float::ConstCache` holds the exact binary-splitting state for the mathematical constants π, ln2, and ln10, so repeated transcendental calls at increasing precision *extend* prior work instead of recomputing from scratch. It is a plain struct of big integers — base-free, `Send` + `Sync` — and a single cache serves any base. `FBig` and `Context` themselves stay `Copy` and carry no cache; the state lives in the separate [`CachedFBig`](./construct.md#cached-arithmetic-for-fbig) wrapper (as `Rc>`), or you can drive a bare `ConstCache` directly. +`dashu_float::ConstCache` holds the exact binary-splitting state for the mathematical constants π, ln2, and ln10, so repeated transcendental calls at increasing precision *extend* prior work instead of recomputing from scratch. It is a plain struct of big integers — base-free, `Send` + `Sync` — and a single cache serves any base. `FBig` and `Context` themselves stay `Copy` and carry no cache; the state lives in the separate [`CachedFBig`](./cached.md) wrapper (as `Rc>`), or you can drive a bare `ConstCache` directly. ### FpResult and CfpResult From d7449596b8b864a4cbdd64fa0e1cc0ce6fcea15f Mon Sep 17 00:00:00 2001 From: Jacob Zhong Date: Mon, 6 Jul 2026 12:34:24 +0800 Subject: [PATCH 11/21] Guide: promote cached.md subsections to H2 (now a top-level chapter) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that Cached Arithmetic is its own chapter, the redundant `## Cached Arithmetic for FBig` title heading is removed (the title comes from SUMMARY.md) and the seven subsections — Creation, Cache sharing, Inspecting/clearing, More constructors, Computing constants, Thread safety, Worked example — are promoted from H3 to H2, matching construct.md's shape. Co-Authored-By: Claude --- guide/src/cached.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/guide/src/cached.md b/guide/src/cached.md index 5859ca17..a0ecf08d 100644 --- a/guide/src/cached.md +++ b/guide/src/cached.md @@ -1,12 +1,10 @@ -## Cached Arithmetic for FBig - The [`CachedFBig`] type is an [`FBig`] that carries a shared handle to a `Rc>`. The cache stores exact binary-splitting state for mathematical constants (π, ln2, ln10), so that transcendental operations (`ln`, `exp`, `sin`, `cos`, …, `pi`) reuse and progressively extend prior work instead of recomputing from scratch. -### Creation +## Creation A `CachedFBig` is created by attaching a cache handle to an `FBig`: @@ -41,7 +39,7 @@ To drop the cache and get back a plain `FBig`, use `into_fbig()` or the let plain: FBig = cached.into(); // or cached.into_fbig() ``` -### Cache sharing +## Cache sharing Binary operations between `CachedFBig` values preserve the cache handle in the result: `(a + b).ln().exp()` keeps extending the same cache throughout. @@ -60,7 +58,7 @@ let result = cached + 3u8; // CachedFBig, cache preserved let result = 10i32 * cached; // CachedFBig, cache preserved ``` -### Inspecting and clearing the cache +## Inspecting and clearing the cache Use `cache()` to borrow the cache read-only and inspect its size: @@ -77,7 +75,7 @@ cached.clear_cache(); assert_eq!(cached.cache().total_terms(), 0); ``` -### More constructors and accessors +## More constructors and accessors Beyond `into_cached` / `with_cache` / `From`, `CachedFBig` mirrors the rest of `FBig`'s construction surface while preserving the cache handle: @@ -86,7 +84,7 @@ Beyond `into_cached` / `with_cache` / `From`, `CachedFBig` mirrors the res - `as_fbig()` — borrow the inner `FBig` immutably (cheap; no cache detach). - `from_repr(repr, context, cache)` / `into_repr()` — the raw-repr constructor/destructor that share a specific cache handle. -### Computing constants directly +## Computing constants directly The cache stores exact binary-splitting state for the constants π, ln2, and ln10, so the methods that produce them reuse and progressively extend prior work rather than recomputing from scratch. On `CachedFBig`, π is a single call: @@ -117,11 +115,11 @@ let ln10 = cache.ln10::<10, HalfAway>(100); `ln_base::(precision)` dispatches to the cached ln2 / ln10 when `B` is 2 or 10 (or a power of two), and falls back to a direct `ln(B)` otherwise. -### Thread safety +## Thread safety `CachedFBig` carries its cache as `Rc>`, so it is **`!Send + !Sync`** — a cached value cannot move across threads. `FBig` itself stays `Copy + Send + Sync` (which is why `static_fbig!` keeps working); only the cached wrapper is non-thread-safe. `ConstCache` is a plain struct of big integers and is itself `Send + Sync`, so to share one cache across threads, wrap a `ConstCache` (or a `CachedFBig`) in `Arc>`. The underlying `Context` methods accept `Option<&mut ConstCache>` regardless of the container, so this needs no API change. -### Worked example: reusing constants across a chain +## Worked example: reusing constants across a chain Because every value-producing operation preserves the cache handle, a chain of transcendentals reuses the same constants throughout. Building several results from one shared handle pays for each constant once: From 49505f4030f3eca509c65b653acb0fc84ca3d3ab Mon Sep 17 00:00:00 2001 From: Jacob Zhong Date: Mon, 6 Jul 2026 12:43:58 +0800 Subject: [PATCH 12/21] Guide/convert: fold CBig into the type-conversion table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop the standalone "Conversion for CBig" section and represent CBig in the "Conversion among Types" table instead — added a CBig row (From FBig/UBig/IBig) and column (TryFrom to FBig/IBig), which is where its big-to-big conversions actually live. Co-Authored-By: Claude --- guide/src/convert.md | 34 +++++++--------------------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/guide/src/convert.md b/guide/src/convert.md index e887fad3..bd2b90ca 100644 --- a/guide/src/convert.md +++ b/guide/src/convert.md @@ -6,12 +6,13 @@ Note that a general principle of implementations of `TryFrom` in `dashu` is that Most of the time, you can use `From`/`Into`/`TryFrom`/`TryInto` to convert between these types. When the conversion is fallible, only `TryFrom` and `TryInto` will be implemented. Below is a table of conversions between arbitrary precision types using these traits, where the columns are source types, and rows are destination types. -| Dest\Src | UBig | IBig | FBig/DBig | RBig | -|-----------|------|---------|--------------|-------------| -| UBig | \ | TryFrom | TryFrom | TryFrom | -| IBig | From | \ | TryFrom | TryFrom | -| FBig/DBig | From | From | \ | TryFrom[^a] | -| RBig | From | From | TryFrom[^a] | \ | +| Dest\Src | UBig | IBig | FBig/DBig | RBig | CBig | +|-----------|------|---------|--------------|-------------|---------| +| UBig | \ | TryFrom | TryFrom | TryFrom | — | +| IBig | From | \ | TryFrom | TryFrom | TryFrom | +| FBig/DBig | From | From | \ | TryFrom[^a] | TryFrom | +| RBig | From | From | TryFrom[^a] | \ | — | +| CBig | From | From | From | — | \ | > [^a]: To use the conversion between `RBig` and `FBig`, the optional feature `dashu-float` must be enabled for the `dashu-ratio` crate. @@ -114,24 +115,3 @@ To a primitive float, `to_f32()` / `to_f64()` return `Rounded` / `Rounded for RBig` succeeds only when the float is exactly rational-representable, and `RBig::to_float()` is the rounding-aware path in the other direction. For approximating a float by a *simple* rational (the smallest numerator/denominator within a tolerance), use `simplest_from_f32` / `simplest_from_f64`, or the interval queries `simplest_in`, `nearest_in`, `next_up`, and `next_down` on `FBig`/`DBig` — these treat the float's own rounding interval as the search bound. - -### Conversion for CBig - -A `CBig` is reached losslessly from any real value: `From`, `From`, and `From` embed the value as the real part with imaginary `+0` (exact, unlimited precision). The inverse is fallible — `TryFrom for FBig` extracts the real part only when the imaginary part is zero (both `±0` count), and `TryFrom for IBig` further requires the real part to be integer-valued. Both compose the `CBig → FBig → IBig` chain, mirroring `FBig`'s own `From`/`TryFrom` split. - -```rust -use dashu_cmplx::CBig; -use dashu_float::{FBig, round::mode::HalfAway}; - -type C = CBig; -type F = FBig; - -// a real value embeds as a purely-real complex number -let z = C::from(F::from(7)); -assert_eq!(z.re().significand(), &7.into()); -assert!(z.im().is_zero()); - -// extracting the real part fails when the imaginary part is nonzero -let w = C::from_parts(F::from(3), F::from(4)); -assert!(F::try_from(w).is_err()); -``` From 3302b49ccdd40ba2f6ea706b0f4adc29cfc7ef5d Mon Sep 17 00:00:00 2001 From: Jacob Zhong Date: Mon, 6 Jul 2026 17:36:29 +0800 Subject: [PATCH 13/21] Implement TryFrom for UBig MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the missing CBig → UBig conversion: it succeeds only when the value is purely real, finite, integer-valued, and non-negative, composing CBig → FBig → UBig (mirroring the existing TryFrom for IBig). Covered by a new try_from_ubig_composes test (ok / negative→OutOfBounds / fractional and nonzero-imaginary→LossOfPrecision); clippy-clean. - complex/src/convert.rs: impl + test. - complex/CHANGELOG.md: Unreleased ### Add entry. - guide/src/convert.md: the CBig→UBig table cell is now "TryFrom" (was "—"). Co-Authored-By: Claude --- complex/CHANGELOG.md | 2 ++ complex/src/convert.rs | 31 +++++++++++++++++++++++++++++++ guide/src/convert.md | 2 +- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/complex/CHANGELOG.md b/complex/CHANGELOG.md index be88b374..434ad637 100644 --- a/complex/CHANGELOG.md +++ b/complex/CHANGELOG.md @@ -4,6 +4,8 @@ ### Add - `CBig::NEG_ONE` (`-1 + 0i`), mirroring `FBig::NEG_ONE`. +- `TryFrom for UBig` — extracts the unsigned integer when the value is purely real, finite, + integer-valued, and non-negative (composes `CBig → FBig → UBig`). ### Improve - The complex `sin_cos` kernel now calls `dashu-float`'s combined `sinh_cosh` (new in `dashu-float`) diff --git a/complex/src/convert.rs b/complex/src/convert.rs index d7d111e1..f422e361 100644 --- a/complex/src/convert.rs +++ b/complex/src/convert.rs @@ -68,6 +68,18 @@ impl TryFrom> for IBig { } } +impl TryFrom> for UBig { + type Error = ConversionError; + + /// Extract an unsigned integer, succeeding only when the number is purely real, finite, + /// integer-valued, and non-negative. Composes [`CBig`] → [`FBig`] → [`UBig`]. + #[inline] + fn try_from(z: CBig) -> Result { + let re: FBig = FBig::try_from(z)?; + UBig::try_from(re) + } +} + #[cfg(test)] mod tests { use super::*; @@ -115,4 +127,23 @@ mod tests { let z = C::from_parts(9.into(), 1.into()); assert_eq!(IBig::try_from(z), Err(ConversionError::LossOfPrecision)); } + + #[test] + fn try_from_ubig_composes() { + let z: C = IBig::from(9).into(); + let u: UBig = UBig::try_from(z).unwrap(); + assert_eq!(u, UBig::from(9u8)); + + // negative real part → OutOfBounds + let z: C = IBig::from(-9).into(); + assert_eq!(UBig::try_from(z), Err(ConversionError::OutOfBounds)); + + // fractional real part → LossOfPrecision + let z = C::from(F::from_parts(123.into(), -2)); // 1.23 + assert_eq!(UBig::try_from(z), Err(ConversionError::LossOfPrecision)); + + // nonzero imaginary → LossOfPrecision + let z = C::from_parts(9.into(), 1.into()); + assert_eq!(UBig::try_from(z), Err(ConversionError::LossOfPrecision)); + } } diff --git a/guide/src/convert.md b/guide/src/convert.md index bd2b90ca..b2f8f7c2 100644 --- a/guide/src/convert.md +++ b/guide/src/convert.md @@ -8,7 +8,7 @@ Most of the time, you can use `From`/`Into`/`TryFrom`/`TryInto` to convert betwe | Dest\Src | UBig | IBig | FBig/DBig | RBig | CBig | |-----------|------|---------|--------------|-------------|---------| -| UBig | \ | TryFrom | TryFrom | TryFrom | — | +| UBig | \ | TryFrom | TryFrom | TryFrom | TryFrom | | IBig | From | \ | TryFrom | TryFrom | TryFrom | | FBig/DBig | From | From | \ | TryFrom[^a] | TryFrom | | RBig | From | From | TryFrom[^a] | \ | — | From 683381d7051ab9029edce1876b0a1c7f6403b71d Mon Sep 17 00:00:00 2001 From: Jacob Zhong Date: Mon, 6 Jul 2026 17:47:10 +0800 Subject: [PATCH 14/21] Implement CBig primitive conversions (both directions) Add direct primitive <-> CBig conversions, all composing through FBig and mirroring dashu-float's surface: - From for CBig (integers, any base) - TryFrom/ for CBig (base-2; NaN rejected, infinities preserved) - TryFrom for every integer primitive (any base) and for f32/f64 (base-2) Generated by two local macros (dashu-float's conversion macros are crate-private, so they can't be reused). Covered by a new primitive_conversions test; clippy-clean. Guide: the primitive conversion tables now include a CBig row (fulfilling the earlier request, now that the impls exist), and complex CHANGELOG records it. Co-Authored-By: Claude --- complex/CHANGELOG.md | 3 ++ complex/src/convert.rs | 80 ++++++++++++++++++++++++++++++++++++++++++ guide/src/convert.md | 4 ++- 3 files changed, 86 insertions(+), 1 deletion(-) diff --git a/complex/CHANGELOG.md b/complex/CHANGELOG.md index 434ad637..1bdba94c 100644 --- a/complex/CHANGELOG.md +++ b/complex/CHANGELOG.md @@ -6,6 +6,9 @@ - `CBig::NEG_ONE` (`-1 + 0i`), mirroring `FBig::NEG_ONE`. - `TryFrom for UBig` — extracts the unsigned integer when the value is purely real, finite, integer-valued, and non-negative (composes `CBig → FBig → UBig`). +- Primitive conversions to/from `CBig`: `From` for the integer primitives (`u8`–`u128`, `i8`–`i128`), + `TryFrom`/`TryFrom` (base 2), and `TryFrom` for every integer and float primitive + (float out is base-2). All compose through `FBig`, mirroring `dashu-float`'s primitive surface. ### Improve - The complex `sin_cos` kernel now calls `dashu-float`'s combined `sinh_cosh` (new in `dashu-float`) diff --git a/complex/src/convert.rs b/complex/src/convert.rs index f422e361..8de87895 100644 --- a/complex/src/convert.rs +++ b/complex/src/convert.rs @@ -80,6 +80,58 @@ impl TryFrom> for UBig { } } +// Conversions between `CBig` and the integer primitives (both directions), composing through +// `FBig`. Integers embed as purely-real complex numbers; extraction succeeds only when the value +// is purely real, finite, in range, and integer-valued. +macro_rules! impl_cbig_int_conv { + ($($t:ty)*) => {$( + impl From<$t> for CBig { + #[inline] + fn from(v: $t) -> Self { + FBig::from(v).into() + } + } + + impl TryFrom> for $t { + type Error = ConversionError; + + #[inline] + fn try_from(z: CBig) -> Result { + let re: FBig = FBig::try_from(z)?; + re.try_into() + } + } + )*}; +} +impl_cbig_int_conv!(u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize); + +// Conversions between `CBig` and `f32`/`f64` (both directions, base 2 only). NaN is rejected on +// input; infinities are preserved. Extraction succeeds only when purely real and exactly +// representable. +macro_rules! impl_cbig_float_conv { + ($($t:ty)*) => {$( + impl TryFrom<$t> for CBig { + type Error = ConversionError; + + #[inline] + fn try_from(f: $t) -> Result { + Ok(CBig::from(FBig::try_from(f)?)) + } + } + + impl TryFrom> for $t { + type Error = ConversionError; + + #[inline] + fn try_from(z: CBig) -> Result { + let re: FBig = FBig::try_from(z)?; + re.try_into() + } + } + )*}; +} +impl_cbig_float_conv!(f32 f64); + #[cfg(test)] mod tests { use super::*; @@ -146,4 +198,32 @@ mod tests { let z = C::from_parts(9.into(), 1.into()); assert_eq!(UBig::try_from(z), Err(ConversionError::LossOfPrecision)); } + + #[test] + fn primitive_conversions() { + // integers embed as purely-real complex numbers (any base) + let z: C = 7u8.into(); + assert_eq!(z.re().significand(), &7.into()); + let z: C = (-3i8).into(); + assert_eq!(z.re().significand(), &(-3i32).into()); + + // floats embed into a base-2 CBig; NaN is rejected + let z = CBig::::try_from(2.5f64).unwrap(); + assert_eq!(z.re().significand(), &5.into()); // 2.5 = 5 * 2^-1 + assert!(CBig::::try_from(f32::NAN).is_err()); + + // CBig -> integer primitive (fails on negative / out-of-range / fractional / nonzero-imag) + assert_eq!(u8::try_from(C::from(9u8)), Ok(9u8)); + assert_eq!(u8::try_from(C::from(-9i8)), Err(ConversionError::OutOfBounds)); + assert_eq!(u8::try_from(C::from(300u16)), Err(ConversionError::OutOfBounds)); + assert_eq!(i8::try_from(C::from(-9i8)), Ok(-9i8)); + assert_eq!( + i8::try_from(C::from(F::from_parts(123.into(), -2))), // 1.23 + Err(ConversionError::LossOfPrecision) + ); + + // CBig -> float primitive (base-2 only) + let z = CBig::::try_from(2.5f64).unwrap(); + assert_eq!(f64::try_from(z), Ok(2.5)); + } } diff --git a/guide/src/convert.md b/guide/src/convert.md index b2f8f7c2..8ef5c55e 100644 --- a/guide/src/convert.md +++ b/guide/src/convert.md @@ -48,8 +48,9 @@ To convert from primitive to big numbers: | IBig | From | From | TryFrom | | FBig/DBig | From | From | TryFrom* | | RBig | From | From | TryFrom | +| CBig | From | From | TryFrom* | -> *: The conversion from `f32`/`f64` to `FBig` is **only defined in base 2**, because the conversion is almost always lossy when the base is not a power of two. To convert from `f32`/`f64` to big floats with other bases (such as `DBig` with base 10), the conversion can be achieved by converting to base 2 first, and then use the `.with_base()` method to convert to other bases. By this way, the rounding during the conversion can be explicitly selected. +> *: The conversion from `f32`/`f64` to `FBig`/`CBig` is **only defined in base 2**, because the conversion is almost always lossy when the base is not a power of two. To convert from `f32`/`f64` to big floats with other bases (such as `DBig` with base 10), the conversion can be achieved by converting to base 2 first, and then use the `.with_base()` method to convert to other bases. By this way, the rounding during the conversion can be explicitly selected. To convert from big numbers to primitive numbers: @@ -59,6 +60,7 @@ To convert from big numbers to primitive numbers: | IBig | TryInto | TryInto | TryInto/`.to_f*()` | | FBig/DBig | TryInto | TryInto | TryInto/`.to_f*()` | | RBig | TryInto | TryInto | TryInto/`.to_f*()`/`.to_f*_fast()` | +| CBig | TryInto | TryInto | TryInto | In the table above, `.to_f*()` denotes `.to_f32()` and `.to_f64()`, similarly `.to_f*_fast()` denotes `.to_f32_fast()` and `.to_f64_fast()`. The *fast* methods don't guarantee corrent rounding so that they can be faster. It's recommended to use the `.to_f*()` methods over the `TryFrom`/`TryInto` trait, because `.to_f*()` will not fail and it also returns the rounding direction during the conversion (i.e. the sign of the rounding error). From 43a8e9dab5815112c4df6dd8e375a1612d79b280 Mon Sep 17 00:00:00 2001 From: Jacob Zhong Date: Mon, 6 Jul 2026 17:56:02 +0800 Subject: [PATCH 15/21] Guide/convert: clarify CBig's float conversion in the primitive table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The big-to-primitive note recommends .to_f*(), but CBig has no such methods — its only float-conversion path is TryInto (base-2). State that explicitly so the CBig row (TryInto, no .to_f*()) isn't surprising. Co-Authored-By: Claude --- guide/src/convert.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guide/src/convert.md b/guide/src/convert.md index 8ef5c55e..a293b7c5 100644 --- a/guide/src/convert.md +++ b/guide/src/convert.md @@ -62,7 +62,7 @@ To convert from big numbers to primitive numbers: | RBig | TryInto | TryInto | TryInto/`.to_f*()`/`.to_f*_fast()` | | CBig | TryInto | TryInto | TryInto | -In the table above, `.to_f*()` denotes `.to_f32()` and `.to_f64()`, similarly `.to_f*_fast()` denotes `.to_f32_fast()` and `.to_f64_fast()`. The *fast* methods don't guarantee corrent rounding so that they can be faster. It's recommended to use the `.to_f*()` methods over the `TryFrom`/`TryInto` trait, because `.to_f*()` will not fail and it also returns the rounding direction during the conversion (i.e. the sign of the rounding error). +In the table above, `.to_f*()` denotes `.to_f32()` and `.to_f64()`, similarly `.to_f*_fast()` denotes `.to_f32_fast()` and `.to_f64_fast()`. The *fast* methods don't guarantee corrent rounding so that they can be faster. It's recommended to use the `.to_f*()` methods over the `TryFrom`/`TryInto` trait, because `.to_f*()` will not fail and it also returns the rounding direction during the conversion (i.e. the sign of the rounding error). (`CBig` has no `.to_f*()` methods — its only float-conversion path is `TryInto`, which is base-2.) The conversions from and to primitive numbers are also implemented for the `dashu_float::Repr` type. Especially `.to_f32()` and `.to_f64()` are implemented which follows the default IEEE rounding mode. From a46a1f056948a4581f43eaecfee29d53c6a300db Mon Sep 17 00:00:00 2001 From: Jacob Zhong Date: Mon, 6 Jul 2026 18:12:01 +0800 Subject: [PATCH 16/21] Guide: drop duplicate page-title headings, promote subsections 16 pages opened with a `## Title` identical to their SUMMARY.md entry (e.g. `## Printing`), which rendered as a duplicate of the sidebar title. Remove that title heading (SUMMARY.md provides the title) and promote the page's subsections up one level so they become the top-level sections. Fence-aware transform; verified no H1 was created and no code block was touched. Re-scan confirms 0 remaining duplicates; build is clean. Co-Authored-By: Claude --- guide/src/cheatsheet.md | 14 ++++++-------- guide/src/compliance.md | 34 ++++++++++++++++------------------ guide/src/faq.md | 10 ++++------ guide/src/io/index.md | 2 -- guide/src/io/interop.md | 8 +++----- guide/src/io/parse.md | 10 ++++------ guide/src/io/print.md | 16 +++++++--------- guide/src/io/serialize.md | 8 +++----- guide/src/ops/basic.md | 12 +++++------- guide/src/ops/bit.md | 8 +++----- guide/src/ops/cmp.md | 10 ++++------ guide/src/ops/exp_log.md | 8 +++----- guide/src/ops/index.md | 2 -- guide/src/ops/num_theory.md | 8 +++----- guide/src/ops/trig_n_hyper.md | 6 ++---- guide/src/performance.md | 6 ++---- 16 files changed, 65 insertions(+), 97 deletions(-) diff --git a/guide/src/cheatsheet.md b/guide/src/cheatsheet.md index eb8a2c66..3c0b9bf6 100644 --- a/guide/src/cheatsheet.md +++ b/guide/src/cheatsheet.md @@ -1,8 +1,6 @@ -## Cheatsheet - A dense reference for the dashu numeric types. See the linked pages for detail. -### Types +## Types | Type | Crate | Description | Literal | |------|-------|-------------|---------| @@ -13,7 +11,7 @@ A dense reference for the dashu numeric types. See the linked pages for detail. | `RBig` | dashu-ratio | rational | `rbig!(22/7)` | | `CBig` | dashu-cmplx | complex, base 2 by default | `cbig!(1+2i)` | -### Construction +## Construction | Way | Example | |-----|---------| @@ -23,7 +21,7 @@ A dense reference for the dashu numeric types. See the linked pages for detail. | literal macro | `dbig!(1.5)`, `cbig!(1+2i)` | | raw words | `UBig::from_words(&[3, 2, 1])` | -### Conversion +## Conversion Lossless conversions use `From`; potentially-lossy ones use `TryFrom` (which fails on any precision loss). See [Conversion](./convert.md) for the full matrix. @@ -38,7 +36,7 @@ Lossless conversions use `From`; potentially-lossy ones use `TryFrom` (which fai | real → `CBig` | `From` | imaginary part `+0` | | `CBig` → `FBig` | `TryFrom` | fails unless imaginary is zero | -### Operators +## Operators | Type | `+ - * /` | `%` | `<< >>` | `& \| ^ !` | |------|:---:|:---:|:---:|:---:| @@ -47,7 +45,7 @@ Lossless conversions use `From`; potentially-lossy ones use `TryFrom` (which fai | `RBig` | ✓ | — | — | — | | `CBig` | ✓ | — | — | — | -### Formatting +## Formatting | Type | `Display` | `Debug` | Other | |------|-----------|---------|-------| @@ -56,7 +54,7 @@ Lossless conversions use `From`; potentially-lossy ones use `TryFrom` (which fai | `RBig` | `num/den` | — | `in_radix`, `in_expanded` | | `CBig` | `a+bi` | `re:.. im:.. (prec: ..)` | — | -### Key methods +## Key methods | Method | On | Returns | |--------|-----|---------| diff --git a/guide/src/compliance.md b/guide/src/compliance.md index 8128172a..dde61f8a 100644 --- a/guide/src/compliance.md +++ b/guide/src/compliance.md @@ -1,5 +1,3 @@ -## Standards Compliance - This page documents where `dashu`'s numeric types conform to the relevant standards — and where they intentionally deviate. There are two aspects: @@ -12,13 +10,13 @@ The common thread: dashu types are **arbitrary-precision**, so fixed-width-encod standard's rules natural to satisfy, they are satisfied; where they conflict with the arbitrary-precision / no-NaN model, the deviation is noted. -### `dashu-float` and IEEE 754-2008 +## `dashu-float` and IEEE 754-2008 The reference here is IEEE Std 754™-2008 (ISO/IEC/IEEE 60559:2011). -#### Data Model +### Data Model -##### Section 3 — Floating-point formats +#### Section 3 — Floating-point formats | IEEE 754 requirement | Compliance | Notes | |---------------------|-----------|-------| @@ -30,9 +28,9 @@ The reference here is IEEE Std 754™-2008 (ISO/IEC/IEEE 60559:2011). | Subnormals | N/A | Arbitrary-precision significands eliminate the need for subnormals. Any non-zero number is normalized. | | Fixed-width encoding | N/A | No fixed bit widths; significands are unbounded `IBig` integers. | -#### Arithmetic Operations +### Arithmetic Operations -##### Section 5 — Operations +#### Section 5 — Operations | IEEE 754 requirement | Compliance | Notes | |---------------------|-----------|-------| @@ -53,7 +51,7 @@ The reference here is IEEE Std 754™-2008 (ISO/IEC/IEEE 60559:2011). | Cancellation under roundTowardNegative → `-0` | ✅ | `cancel_zero` in add.rs produces `-0` when `R::IS_ROUND_TOWARD_NEGATIVE`. | | Exact subtraction cancels to `-0` only under directed rounding | ✅ | IEEE 754 §6.3: `(-3) + 3` = `+0` under roundTiesToEven/Up, `-0` under Down. | -##### Section 5.3 — Rounding +#### Section 5.3 — Rounding | IEEE 754 requirement | Compliance | Notes | |---------------------|-----------|-------| @@ -61,7 +59,7 @@ The reference here is IEEE Std 754™-2008 (ISO/IEC/IEEE 60559:2011). | Correct rounding to within 1 ulp | ✅ | All operations guarantee $|error| < 1\text{ ulp}$. The `Rounded` type distinguishes exact from inexact results. | | Round-to-nearest preserves sign of zero | ✅ | `rounded_to_repr` preserves input sign when rounding collapses a non-zero to zero. | -##### Section 5.6 — Sign bit operations +#### Section 5.6 — Sign bit operations | IEEE 754 requirement | Compliance | Notes | |---------------------|-----------|-------| @@ -70,7 +68,7 @@ The reference here is IEEE Std 754™-2008 (ISO/IEC/IEEE 60559:2011). | `signum(±0)` = `+0` | ✅ | Returns `+0` for both `+0` and `-0` (signum collapses the sign of zero). | | `sign()` distinguishes `+0` from `-0` | ✅ | `Repr::sign()` returns `Negative` for `-0`. | -#### Conversions +### Conversions | IEEE 754 requirement | Compliance | Notes | |---------------------|-----------|-------| @@ -81,7 +79,7 @@ The reference here is IEEE Std 754™-2008 (ISO/IEC/IEEE 60559:2011). | Int-to-float conversion exact for representable integers | ✅ | | | Float-to-int overflows saturate (per Rust convention) | N/A | Rust's `TryFrom` returns an error on overflow; `ToPrimitive` returns `None`. | -#### Exceptional Conditions +### Exceptional Conditions | IEEE 754 requirement | Compliance | Notes | |---------------------|-----------|-------| @@ -91,7 +89,7 @@ The reference here is IEEE Std 754™-2008 (ISO/IEC/IEEE 60559:2011). | Underflow → `±0` (no trap) | ✅ | Same. | | Inexact flag | ⚠️ Partial | The `Rounded` type carries `Exact`/`Inexact(T, Rounding)` to signal whether rounding occurred, but there is no sticky flag mechanism. | -#### Summary (dashu-float) +### Summary (dashu-float) | Category | Status | |----------|--------| @@ -104,7 +102,7 @@ The reference here is IEEE Std 754™-2008 (ISO/IEC/IEEE 60559:2011). | Subnormals | N/A (unbounded precision) | | Exception flags | ⚠️ Rounded type signals exact/inexact, no sticky flags | -### `dashu-cmplx` and C99 Annex G +## `dashu-cmplx` and C99 Annex G `CBig` is a pair of `Repr` parts (real, imaginary) over a single shared precision and rounding mode. It targets C99 Annex G (IEC 60559-compatible complex arithmetic) for the common functionality, @@ -112,7 +110,7 @@ reusing `dashu-float`'s signed-zero / signed-infinity / branch-cut machinery for `FBig`, there is **no NaN**: C99 cases that would produce a complex NaN are mapped to `FpError` at the `Context` layer (and panics at the convenience layer). -#### Data Model (§G.2) +### Data Model (§G.2) | C99 Annex G requirement | Compliance | Notes | |---------------------|-----------|-------| @@ -122,7 +120,7 @@ the `Context` layer (and panics at the convenience layer). | A single complex infinity (Riemann point) | ✅ | `proj` collapses any part-infinite value to `+∞ + i·0`; overflow yields both parts `+∞`. | | Complex NaN | ❌ Deviates | No NaN. NaN-producing cases map to `FpError` (`Context`) / panic (convenience layer). | -#### Arithmetic (§G.5) +### Arithmetic (§G.5) | C99 Annex G requirement | Compliance | Notes | |---------------------|-----------|-------| @@ -135,7 +133,7 @@ the `Context` layer (and panics at the convenience layer). | `finite/∞`, `0/finite` → `0` | ✅ | | | `1/0 → ∞`, `1/∞ → 0` (inverse) | ✅ | | -#### Transcendentals and branch cuts (§G.6) +### Transcendentals and branch cuts (§G.6) | C99 Annex G requirement | Compliance | Notes | |---------------------|-----------|-------| @@ -148,7 +146,7 @@ the `Context` layer (and panics at the convenience layer). | $\arg(0 + i\cdot\infty) = +\pi/2$, $\arg(0 - i\cdot\infty) = -\pi/2$ | ✅ | `arg = atan2(im, re)`, reusing `dashu-float`'s Annex-G `atan2` table. | | `abs`/`hypot` overflow-safe modulus | ✅ | Thin composition over `dashu-float`'s `hypot`. | -#### Exceptional Conditions +### Exceptional Conditions | C99 Annex G requirement | Compliance | Notes | |---------------------|-----------|-------| @@ -156,7 +154,7 @@ the `Context` layer (and panics at the convenience layer). | Domain error (e.g. even root of a negative value, out-of-range inverse trig) | ❌ Deviates | `Err(FpError::OutOfDomain)` / panic, rather than a NaN result. | | Each component rounded independently to the shared mode | ✅ | Near-correctly rounded per axis (a guaranteed-correct Ziv loop is deferred to 0.5.x). | -#### Summary (dashu-cmplx) +### Summary (dashu-cmplx) | Category | Status | |----------|--------| diff --git a/guide/src/faq.md b/guide/src/faq.md index e31ec5f0..be730aa3 100644 --- a/guide/src/faq.md +++ b/guide/src/faq.md @@ -1,10 +1,8 @@ -## FAQ - -### Why is the library called `dashu`? +## Why is the library called `dashu`? `dashu` is the pinyin romanization of 大数 ("dà shù"), Chinese for *big number*. -### Why to use `dashu`? +## Why to use `dashu`? `dashu` aims to be a Rust-native, ergonomic alternative to GNU GMP + MPFR + MPC: arbitrary-precision integers, floats, rationals, and complex numbers, all in pure Rust with full `no_std` support and arbitrary-base floats. @@ -19,13 +17,13 @@ Compared with other Rust crates: `malachite` also offers pure-Rust integers and rationals with a performance focus, but is `std`-oriented and does not cover arbitrary-precision floats or complex numbers. Unlike `rug`, `dashu` has no C dependency — it builds and runs anywhere Rust does, including `no_std` targets. -### Known limitations +## Known limitations - **No NaN.** Invalid operations panic at the convenience layer and return `Err(FpError)` at the context layer. Infinities are terminal values, not operands — see [Standards Compliance](./compliance.md). - **Near-correct rounding.** Transcendentals are rounded within 1 ulp via a guard-digit recipe; a guaranteed-correct Ziv loop is planned for a later release. - **Complex surface.** `CBig` ships field arithmetic and the elementary transcendentals; complex hyperbolics, `fma`, and several others are deferred to 0.5.x (see the v0.5 release notes). - **No SIMD-FFT multiplication** yet (planned for v1.0). -### MSRV and feature policy +## MSRV and feature policy The current MSRV is **1.68**. Third-party integrations follow a versioned-feature convention: stable dependencies use `xxx_vYY` (e.g. `rand_v08`) with an unversioned `xxx` alias pinned to one version, while unstable dependencies alias `xxx` to the newest. See [Cargo Features](./index.md#cargo-features) for the full explanation. diff --git a/guide/src/io/index.md b/guide/src/io/index.md index aaffc9f6..8dcb5eb8 100644 --- a/guide/src/io/index.md +++ b/guide/src/io/index.md @@ -1,5 +1,3 @@ -## Input and Output - dashu's numeric types participate in Rust's standard formatting and parsing traits, plus a few dashu-specific APIs for radix conversion, positional expansion, and byte-level serialization. This section covers: - [Parsing](./parse.md) — `FromStr` and `from_str_radix` for every type, including the float exponent forms. diff --git a/guide/src/io/interop.md b/guide/src/io/interop.md index 2cf8fa55..64a6ef95 100644 --- a/guide/src/io/interop.md +++ b/guide/src/io/interop.md @@ -1,8 +1,6 @@ -## Interoperability - Besides the standard formatting and parsing traits, `dashu-int` exposes lower-level access to a `UBig`'s raw representation, for interoperating with other libraries or building custom (de)serialization. -### Digit access +## Digit access `UBig::to_digits(base)` returns the number's digits in any base `2..=Word::MAX` (most-significant first, stored as `Word`), and `UBig::from_digits(base, &digits)` reconstructs it. This generalizes `in_radix` (which is limited to base 2–36 for string output) to arbitrary bases and word-sized digits. @@ -14,11 +12,11 @@ let digits = n.to_digits(16); // [1, 2, 3, 4], most-significant first assert_eq!(UBig::from_digits(16, &digits)?, n); ``` -### Byte access +## Byte access `to_le_bytes` / `to_be_bytes` and `from_le_bytes` / `from_be_bytes` give a portable, explicit-endianness byte representation — see [Serialization](./serialize.md). -### Word access +## Word access `UBig::from_words(&[w0, w1, …])` builds a value from little-endian words, and `.as_words()` borrows the underlying word slice without copying. This is the closest to the raw in-memory form. diff --git a/guide/src/io/parse.md b/guide/src/io/parse.md index f226b81b..ee4f401f 100644 --- a/guide/src/io/parse.md +++ b/guide/src/io/parse.md @@ -1,8 +1,6 @@ -## Parsing - Every numeric type implements `FromStr`, so values can be built with `"...".parse()?` or `T::from_str(...)`. Underscore separators are allowed in all numeric literals. -### Parsing Integers +## Parsing Integers `UBig::from_str` / `IBig::from_str` accept an optional sign followed by decimal digits. For other bases use `from_str_radix(s, radix)` (radix 2–36); it recognizes a `0x`/`0o`/`0b` prefix independently of the `radix` argument. @@ -14,7 +12,7 @@ assert_eq!(UBig::from_str("12345")?, UBig::from(12345u16)); assert_eq!(IBig::from_str_radix("-1aff", 16)?, IBig::from(-0x1aff)); ``` -### Parsing Floats +## Parsing Floats `FBig`/`DBig` `FromStr` reads the significand in the value's native base, with the exponent in one of these forms: @@ -35,7 +33,7 @@ assert_eq!(format!("{:e}", DBig::from_str("6.022e23")?), "6.022e23"); assert_eq!(DBig::from_str("-0.0123456789")?.to_string(), "-0.0123456789"); ``` -### Parsing Rationals +## Parsing Rationals `RBig::from_str` accepts `numerator/denominator`, or just a numerator (denominator defaults to 1). `from_str_radix` parses both parts in the given base; a `0x`/`0o`/`0b` prefix must be consistent between them. @@ -46,7 +44,7 @@ use core::str::FromStr; assert_eq!(RBig::from_str("22/7")?.to_string(), "22/7"); ``` -### Parsing Complex +## Parsing Complex `CBig` `FromStr` accepts the same algebraic $a+bi$ grammar that `Display` emits: an optional real term plus an optional signed imaginary term (at least one required); a unit coefficient may be omitted (`i`, `-i`). The MPC-style parenthesized form `(re im)` is **not** accepted. diff --git a/guide/src/io/print.md b/guide/src/io/print.md index 449f612f..920b27e7 100644 --- a/guide/src/io/print.md +++ b/guide/src/io/print.md @@ -1,8 +1,6 @@ -## Printing - `UBig` and `IBig` support the full set of Rust standard formatter traits: `Display`, `Debug`, `Binary`, `Octal`, `LowerHex`, `UpperHex`. The float, rational, and complex types support `Display` and `Debug`, with extra radix/positional helpers described below. All of them honor the sign, width, fill, padding, and alignment options of `Formatter`. -### Integer Formatting +## Integer Formatting `Display` renders a `UBig`/`IBig` in decimal. The `Binary`, `Octal`, `LowerHex`, and `UpperHex` traits render in base 2/8/16, with the `#` flag adding the conventional `0b`/`0o`/`0x`/`0X` prefix. For any other radix, use `in_radix(r)` (base 2–36); its `#` flag uppercases digits above 9. @@ -18,13 +16,13 @@ assert_eq!(format!("{}", n.in_radix(16)), "ff"); assert_eq!(format!("{:#}", n.in_radix(16)), "FF"); ``` -### Debug Print +## Debug Print The `Debug` implementation uses a compact **head‥tail** format for large integers: it prints the most significant digits, a `..` separator, and the least significant digits, omitting the middle. For small integers that fit in a single `Word` or `DoubleWord` the full number is shown without truncation. There are two forms, controlled by the formatter flags: -#### Simple form (`{:?}`) +### Simple form (`{:?}`) Shows the truncated head‥tail representation. @@ -48,7 +46,7 @@ assert_eq!( The number of digits shown on each end depends on the `Word` size — on 64-bit targets it is 19 decimal digits at each end (one word's worth), on 32-bit targets it is 9 digits. -#### Verbose form (`{:#?}`) +### Verbose form (`{:#?}`) Adds `(digits: N, bits: M)` after the head‥tail representation, showing the total digit count and bit length. @@ -64,7 +62,7 @@ if Word::BITS == 64 { } ``` -### Float Formatting +## Float Formatting `FBig`/`DBig` `Display` renders the significand with the radix point positioned by the exponent — the natural positional form, not scientific. The formatter precision option rounds to that many fractional digits. @@ -86,7 +84,7 @@ assert_eq!(format!("{:e}", DBig::from_str("1234.5")?), "1.2345e3"); assert_eq!(format!("{:E}", DBig::from_str("1234.5")?), "1.2345E3"); ``` -### Rational Formatting +## Rational Formatting `RBig`/`Relaxed` `Display` renders as `numerator/denominator`, or just the numerator when the denominator is `1`. The `Binary`/`Octal`/`LowerHex`/`UpperHex` traits and `in_radix(r)` format both parts in the given base. @@ -108,7 +106,7 @@ assert_eq!(format!("{:.4}", x.in_expanded()), "0.3333"); assert_eq!(format!("{:#}", x.in_expanded()), "0.(3)"); ``` -### Complex Formatting +## Complex Formatting `CBig` `Display` uses the algebraic $a+bi$ notation: the imaginary term always carries an explicit sign, a unit coefficient is elided (`i`, not `1i`), and a zero imaginary part is omitted. `Debug` prints `re: im: (prec:

)`. diff --git a/guide/src/io/serialize.md b/guide/src/io/serialize.md index aa1be213..3a2f4951 100644 --- a/guide/src/io/serialize.md +++ b/guide/src/io/serialize.md @@ -1,12 +1,10 @@ -## Serialization - ```text The layout for serialized numbers is protected by semver. A change to the layout is considered a breaking change and a new major version will be published. ``` dashu offers three layers of (de)serialization for its integer and float types, chosen by how portable or fast the format must be. -### Conversion to Bytes +## Conversion to Bytes `UBig` and `IBig` convert to and from explicit-endianness byte sequences via `to_le_bytes` / `to_be_bytes` and `from_le_bytes` / `from_be_bytes`. These are portable, layout-stable formats suitable for binary interchange. @@ -18,10 +16,10 @@ let bytes = n.to_le_bytes(); assert_eq!(UBig::from_le_bytes(&bytes), n); ``` -### Serialization with `serde` +## Serialization with `serde` With the `serde` feature enabled, every numeric type implements `Serialize` / `Deserialize`. The human-readable form (when `is_human_readable()` is true) is a string, for easy use with JSON/TOML; the compact binary form is used otherwise. Only the binary form's layout is semver-protected. -### Serialization with `rkyv` +## Serialization with `rkyv` With the `rkyv` feature enabled, zero-copy (de)serialization is available for the integer types — fastest for same-architecture scenarios, at the cost of a less portable layout. diff --git a/guide/src/ops/basic.md b/guide/src/ops/basic.md index 10de4427..8d3192b7 100644 --- a/guide/src/ops/basic.md +++ b/guide/src/ops/basic.md @@ -1,8 +1,6 @@ -## Basic Arithmetics - The standard arithmetic operators are implemented for all numeric types, for both owned and borrowed operands. The behavior of division and remainder differs by type. -### Integer Arithmetic +## Integer Arithmetic `UBig` and `IBig` support `+`, `-`, `*`, `/`, and `%`. Integer division rounds toward zero, and the remainder takes the sign of the dividend (the C/Rust convention). For Euclidean division (non-negative remainder) use the `DivRemEuclid` / `RemEuclid` traits from `dashu-base`; `DivRem` returns both quotient and remainder at once. @@ -14,15 +12,15 @@ let e = 2 * &b - 1; // mixes naturally with primitives assert_eq!(e, IBig::from(-0x21ff)); ``` -### Float Arithmetic +## Float Arithmetic `FBig`/`DBig` support `+`, `-`, `*`, `/` between values of the **same base and rounding mode** (mixed bases are a compile error by design). The result precision is `max(lhs.precision, rhs.precision)`, and each operation reports its inexactness through the two-layer API described in [Exponential and Logarithm](./exp_log.md). Infinities are terminal: `1/0` and `ln(0)` produce `±∞`, but feeding an infinity back into arithmetic is an error (`FpError::InfiniteInput`). -### Rational Arithmetic +## Rational Arithmetic `RBig` supports `+`, `-`, `*`, `/`. Division by zero panics. `Relaxed` performs the same operations without auto-reducing to lowest terms (faster for a chain of operations); call `canonicalize()` to reduce when needed. -### Complex Arithmetic +## Complex Arithmetic `CBig` supports the field operations `+`, `-`, `*`, `/`, plus `sqr` and `inv` (multiplicative inverse). Multiplication and division by a real `FBig` are also available as mixed-type operators. Multiplication and division use Smith's method with a guard digit and re-round, giving the same near-correctly-rounded guarantee as `dashu-float`'s transcendentals. @@ -36,6 +34,6 @@ let sum = &z + &C::I; // (3+4i) + i = 3+5i assert_eq!(sum.im().significand(), &5.into()); ``` -### Mixed-type arithmetic +## Mixed-type arithmetic There are **no implicit mixed-type operators** between different big-number kinds (e.g. `UBig + FBig` does not compile) — convert explicitly first (see [Conversion](../convert.md)). diff --git a/guide/src/ops/bit.md b/guide/src/ops/bit.md index 3295c7eb..b521be04 100644 --- a/guide/src/ops/bit.md +++ b/guide/src/ops/bit.md @@ -1,5 +1,3 @@ -## Bit Manipulation - `UBig` and `IBig` support the bitwise operators `&` (and), `|` (or), `^` (xor), and `!` (not). On `UBig`, `!` is an *infinite-width* complement — every bit above the highest set bit is treated as `1`, so `!n` is generally a very large number. On `IBig`, `!` follows the two's-complement rule. ```rust @@ -11,15 +9,15 @@ assert_eq!(format!("{:b}", &a & &b), "1000"); assert_eq!(format!("{:b}", &a | &b), "1110"); ``` -### Bit testing and length +## Bit testing and length The `BitTest` trait (from `dashu-base`) tests and measures individual bits: `.bit(n)` returns the `n`-th bit, and `.bit_len()` returns the position of the highest set bit plus one. `set_bit(n)` / `clear_bit(n)` mutate a `UBig` in place, and `trailing_zeros()` counts the low-order zero bits. -### Shifts +## Shifts `<<` and `>>` shift by a `usize`. Left shifts grow the number; right shifts shrink it and are equivalent to floor-division by a power of two. -### Using `UBig` as a bit vector +## Using `UBig` as a bit vector Because a `UBig` has unbounded width, it works naturally as an arbitrarily large bit set: set bit `i` with `set_bit(i)`, test it with `bit(i)`, and read the extent with `bit_len()`. diff --git a/guide/src/ops/cmp.md b/guide/src/ops/cmp.md index 4e664408..f450bed4 100644 --- a/guide/src/ops/cmp.md +++ b/guide/src/ops/cmp.md @@ -1,19 +1,17 @@ -## Equality and Comparison - Comparison is natively enabled **only between big numbers of the same kind**, not between big numbers and primitive types — this avoids the trait-overlap problem described in [`num-bigint`#150](https://github.com/rust-num/num-bigint/issues/150). To compare a big number with a primitive type, enable the `num-order` feature and use the `NumOrd` trait. -### Equality +## Equality `PartialEq`/`Eq` is value equality. For `FBig`/`DBig` it compares the representation and ignores the context (precision and rounding mode), so two floats with different precision but the same value compare equal. Signed zeros compare equal: `+0 == -0`. `CBig` compares componentwise, with `+0 == -0` on each part. -### Ordering +## Ordering `UBig`/`IBig`/`RBig`/`FBig`/`DBig` carry the natural numeric total order (`Ord`). Infinities are placed at the ends: $-\infty < \text{finite} < +\infty$. `CBig` defines a lexicographic total order by `(re, then im)` — usable for sorting and `BTreeMap`, but note it is *not* an algebraic magnitude ordering. -### Sign +## Sign The signed types (`IBig`, `FBig`/`DBig`, `RBig`, `CBig`) expose `.sign()` (returning `dashu_base::Sign`, where zero is `Positive`) and `.signum()` (returning `-1`, `0`, or `+1` as the same type). -### Magnitude comparison and cross-type ordering +## Magnitude comparison and cross-type ordering `AbsOrd` (from `dashu-base`) compares by absolute value; for `CBig` it compares by $|z|$. The `num-order` feature adds `NumOrd` for ordering and `NumHash` for hashing across different numeric types (big and primitive), keeping them consistent with each other. diff --git a/guide/src/ops/exp_log.md b/guide/src/ops/exp_log.md index 3e7901da..b9e1e16b 100644 --- a/guide/src/ops/exp_log.md +++ b/guide/src/ops/exp_log.md @@ -1,15 +1,13 @@ -## Exponential and Logarithm - `FBig`/`DBig` provide the exponential, logarithmic, power, and root families, plus the mathematical constants. `CBig` provides the complex analogs of each. -### Two-layer API +## Two-layer API Like all inexact operations, transcendentals come in two layers (see [types](../types.md)): - **Context layer** — `Context` methods take a `&Repr` and return `FpResult>` (a correctly-rounded result or an `FpError`), carrying the rounding direction. They accept an optional `&mut ConstCache` for constant reuse. - **Convenience layer** — methods on `FBig` (`.exp()`, `.ln()`, …) unwrap to a plain `FBig`, panicking on `Indeterminate`/`OutOfDomain`/`InfiniteInput` and saturating overflow/underflow to `±∞`/`±0`. -### Real functions +## Real functions - Exponential: `exp`, `exp_m1` ($e^x - 1$, accurate near zero). - Logarithm: `ln`, `ln_1p` ($\ln(1+x)$, accurate near zero). @@ -18,7 +16,7 @@ Like all inexact operations, transcendentals come in two layers (see [types](../ (`exp2`/`exp10`/`log2`/`log10` are deferred to a later 0.5.x release.) -### Complex functions +## Complex functions `CBig` mirrors the real set with `exp`, `ln`, `sqrt`, `powi`, and `powf`, built on the real implementations. The identities are diff --git a/guide/src/ops/index.md b/guide/src/ops/index.md index d9d18081..a6856ee7 100644 --- a/guide/src/ops/index.md +++ b/guide/src/ops/index.md @@ -1,5 +1,3 @@ -## Operations - dashu implements a full set of arithmetic, comparison, bitwise, and number-theoretic operations for its numeric types, following standard Rust operator conventions. This section covers each category: - [Equality and Comparison](./cmp.md) — `PartialEq`/`Eq`, ordering, magnitude comparison, and hashing. diff --git a/guide/src/ops/num_theory.md b/guide/src/ops/num_theory.md index e3a6acd9..a1b4fef9 100644 --- a/guide/src/ops/num_theory.md +++ b/guide/src/ops/num_theory.md @@ -1,8 +1,6 @@ -## Number Theoretic - `dashu-int` provides greatest-common-divisor and modular-arithmetic primitives. -### Greatest common divisor +## Greatest common divisor The `Gcd` trait (from `dashu-base`) gives `gcd`, and `ExtendedGcd` gives `gcd_ext`, which returns `(gcd, x, y)` with $a\cdot x + b\cdot y = \gcd(a,b)$. @@ -15,7 +13,7 @@ let b = UBig::from(8u8); assert_eq!((&a).gcd(&b), UBig::from(4u8)); ``` -### Modular arithmetic +## Modular arithmetic For repeated operations against a fixed modulus, precompute a `ConstDivisor` and reduce values into `Reduced`. Addition, subtraction, multiplication, exponentiation, and inversion then run against the precomputed modulus, and the result prints in `(mod N)` form. @@ -28,6 +26,6 @@ let y = ring.reduce(55443); assert_eq!(format!("{}", x - y), "6902 (mod 10000)"); ``` -### Diophantine approximation +## Diophantine approximation Rational approximation of reals — the simplest rational within a tolerance, continued fractions — lives on `RBig`; see [Conversion](../convert.md#conversion-to-rbig) for `simplest_in` / `nearest_in`. diff --git a/guide/src/ops/trig_n_hyper.md b/guide/src/ops/trig_n_hyper.md index 5008fd4d..648f2518 100644 --- a/guide/src/ops/trig_n_hyper.md +++ b/guide/src/ops/trig_n_hyper.md @@ -1,15 +1,13 @@ -## Trigonometric and Hyperbolic Functions - `FBig`/`DBig` and `CBig` provide the trigonometric and hyperbolic functions. They are grouped on one page because the complex circular functions are built from the real circular *and* hyperbolic functions. -### Real functions +## Real functions - Circular: `sin`, `cos`, `tan`, and `sin_cos` (both at once); inverses `asin`, `acos`, `atan`, and the four-quadrant `atan2(y, x)`. - Hyperbolic: `sinh`, `cosh`, `tanh`, `sinh_cosh`; inverses `asinh`, `acosh`, `atanh`. Angles are in radians. `atan2` follows the C99 signed-zero model, which matters for correct branch-cut behavior on the axes. -### Complex functions +## Complex functions `CBig` provides the circular family `sin`, `cos`, `tan`, `sin_cos`, `asin`, `acos`, and `atan`. They are evaluated from the real `sin`/`cos` and `sinh`/`cosh` via the identities diff --git a/guide/src/performance.md b/guide/src/performance.md index b468f1ad..3b700612 100644 --- a/guide/src/performance.md +++ b/guide/src/performance.md @@ -1,5 +1,3 @@ -## Performance - `dashu` aims to be efficient while staying portable. By default it compiles for the generic baseline of each target architecture, so a binary that depends on `dashu` runs on any CPU that the target baseline supports. @@ -7,7 +5,7 @@ the generic baseline of each target architecture, so a binary that depends on When big-number arithmetic is on the hot path, you can get a meaningful speedup by telling the compiler which CPU you are actually running on. -### Build with `target-cpu=native` +## Build with `target-cpu=native` The single most impactful setting is to compile with the host CPU's feature set: @@ -43,7 +41,7 @@ Note that `target-cpu=native` targets the CPU of the **machine doing the build**. If you build on one host and deploy to another, prefer an explicit `target-cpu`/`target-feature` that matches the deployment hardware instead. -### Runtime feature detection (default builds) +## Runtime feature detection (default builds) Even in a default baseline build, `dashu-int`'s hottest basecase multiplication kernels dispatch at runtime to a BMI2 (`mulx`) implementation on x86-64 when From 6fdb288631627e206d749df7a86444052326a8ba Mon Sep 17 00:00:00 2001 From: Jacob Zhong Date: Mon, 6 Jul 2026 18:31:25 +0800 Subject: [PATCH 17/21] Guide: use dashu::module:: paths instead of dashu_X:: Replace the sub-crate paths dashu_base/int/float/ratio/cmplx:: with the meta-crate paths dashu::base/integer/float/rational/complex:: throughout the guide. Also fix index.md's meta-crate section, which had the wrong module names (dashu::int, dashu::ratio) and the wrong alias (dashu::Ratio); it now lists the correct modules and the dashu::Complex alias. Co-Authored-By: Claude --- guide/src/cached.md | 14 +++++++------- guide/src/convert.md | 16 ++++++++-------- guide/src/index.md | 13 +++++++------ guide/src/io/interop.md | 4 ++-- guide/src/io/parse.md | 12 ++++++------ guide/src/io/print.md | 18 +++++++++--------- guide/src/io/serialize.md | 2 +- guide/src/ops/basic.md | 6 +++--- guide/src/ops/bit.md | 6 +++--- guide/src/ops/cmp.md | 2 +- guide/src/ops/num_theory.md | 6 +++--- guide/src/types.md | 28 ++++++++++++++-------------- 12 files changed, 64 insertions(+), 63 deletions(-) diff --git a/guide/src/cached.md b/guide/src/cached.md index a0ecf08d..d6ff5403 100644 --- a/guide/src/cached.md +++ b/guide/src/cached.md @@ -11,7 +11,7 @@ A `CachedFBig` is created by attaching a cache handle to an `FBig`: ```rust use std::rc::Rc; use core::cell::RefCell; -use dashu_float::{CachedFBig, ConstCache, FBig, Repr, Context}; +use dashu::float::{CachedFBig, ConstCache, FBig, Repr, Context}; let cache = Rc::new(RefCell::new(ConstCache::new())); @@ -91,8 +91,8 @@ The cache stores exact binary-splitting state for the constants π, ln2, and ln1 ```rust use std::rc::Rc; use core::cell::RefCell; -use dashu_float::{CachedFBig, ConstCache}; -use dashu_float::round::mode::HalfAway; +use dashu::float::{CachedFBig, ConstCache}; +use dashu::float::round::mode::HalfAway; let cache = Rc::new(RefCell::new(ConstCache::new())); let _pi = CachedFBig::::pi(100, &cache); @@ -103,8 +103,8 @@ let _pi_more = CachedFBig::::pi(1000, &cache); You can also drive a bare `ConstCache` directly, without a `CachedFBig` — useful when you want the constants but not the per-value wrapper. The methods are generic over base and rounding mode, and a single cache serves any base: ```rust -use dashu_float::ConstCache; -use dashu_float::round::mode::HalfAway; +use dashu::float::ConstCache; +use dashu::float::round::mode::HalfAway; let mut cache = ConstCache::new(); let pi = cache.pi::<10, HalfAway>(100).value(); // computes from scratch @@ -126,8 +126,8 @@ Because every value-producing operation preserves the cache handle, a chain of t ```rust use std::rc::Rc; use core::cell::RefCell; -use dashu_float::{CachedFBig, ConstCache, Context, Repr}; -use dashu_float::round::mode::HalfAway; +use dashu::float::{CachedFBig, ConstCache, Context, Repr}; +use dashu::float::round::mode::HalfAway; type F = CachedFBig; let cache = Rc::new(RefCell::new(ConstCache::new())); diff --git a/guide/src/convert.md b/guide/src/convert.md index a293b7c5..f38218e6 100644 --- a/guide/src/convert.md +++ b/guide/src/convert.md @@ -32,7 +32,7 @@ Nevertheless, there are other useful methods for **lossy** conversions: > - [^d] See the section *Conversion from Floats to RBig* below for more approaches. > - [^e] This method requires the `dashu-float` feature to be enabled for the crate `dashu-ratio`. -Another useful conversion is `UBig::as_ibig()`. Due to the fact that `UBig` and `IBig` has the same memory layout, A `UBig` can be directed used as an `IBig` through this method. Similarly, `RBig::as_relaxed()` can be helpful when you want to use an `RBig` instance as an `dashu_ratio::Relaxed`. +Another useful conversion is `UBig::as_ibig()`. Due to the fact that `UBig` and `IBig` has the same memory layout, A `UBig` can be directed used as an `IBig` through this method. Similarly, `RBig::as_relaxed()` can be helpful when you want to use an `RBig` instance as an `dashu::rational::Relaxed`. Besides these methods designed for conversions, the constructors and destructors can also be used for the purpose of type conversion, especially from compound types to its parts. Please refer to the [Construction and Destruction](./construct.md#Construct_from_Parts) page for this approach. @@ -64,7 +64,7 @@ To convert from big numbers to primitive numbers: In the table above, `.to_f*()` denotes `.to_f32()` and `.to_f64()`, similarly `.to_f*_fast()` denotes `.to_f32_fast()` and `.to_f64_fast()`. The *fast* methods don't guarantee corrent rounding so that they can be faster. It's recommended to use the `.to_f*()` methods over the `TryFrom`/`TryInto` trait, because `.to_f*()` will not fail and it also returns the rounding direction during the conversion (i.e. the sign of the rounding error). (`CBig` has no `.to_f*()` methods — its only float-conversion path is `TryInto`, which is base-2.) -The conversions from and to primitive numbers are also implemented for the `dashu_float::Repr` type. Especially `.to_f32()` and `.to_f64()` are implemented which follows the default IEEE rounding mode. +The conversions from and to primitive numbers are also implemented for the `dashu::float::Repr` type. Especially `.to_f32()` and `.to_f64()` are implemented which follows the default IEEE rounding mode. ### Conversion for FBig/DBig @@ -79,9 +79,9 @@ The base, precision, and rounding mode are changed independently: - `with_precision(p)` widens or shrinks the significand to `p` digits. Widening is always exact (`Approximation::Exact`); shrinking rounds per `R` and returns `Approximation::Inexact` carrying the rounding direction. ```rust -use dashu_base::Approximation::*; -use dashu_float::DBig; -use dashu_float::round::Rounding::*; +use dashu::base::Approximation::*; +use dashu::float::DBig; +use dashu::float::round::Rounding::*; let a = DBig::from_str("2.345")?; assert_eq!(a.precision(), 4); @@ -102,9 +102,9 @@ Converting *into* `FBig` from `UBig`/`IBig` (or any primitive integer) infers th Going the other way, `TryFrom for IBig`/`UBig` succeeds only when the float is finite and exactly integer-valued — `ConversionError::OutOfBounds` for infinities, `LossOfPrecision` for a fractional part. For a rounding-aware path use `to_int()`, which always succeeds and reports the rounding direction: ```rust -use dashu_base::Approximation::*; -use dashu_float::DBig; -use dashu_float::round::Rounding::*; +use dashu::base::Approximation::*; +use dashu::float::DBig; +use dashu::float::round::Rounding::*; assert_eq!(DBig::from_str("1234")?.to_int(), Exact(1234.into())); assert_eq!(DBig::from_str("1.234")?.to_int(), Inexact(1.into(), NoOp)); diff --git a/guide/src/index.md b/guide/src/index.md index 197d1a96..96913e1f 100644 --- a/guide/src/index.md +++ b/guide/src/index.md @@ -17,12 +17,13 @@ To make it useful for every Rust user, it features: ### The meta crate -The crate `dashu` is a meta crate that exposes all the functionalities of the subcrates (`dashu-base`, `dashu-int`, `dashu-float`, `dashu-ratio` and `dashu-macros`). Each subcrate becomes a module in `dashu`, for example, `dashu-int` is re-exported as `dashu::int`. Besides, it creates more readable aliases for the numeric types: -- `dashu::Natural` = `dashu::int::UBig` = `dashu_int::UBig` -- `dashu::Integer` = `dashu::int::IBig` = `dashu_int::IBig` -- `dashu::Ratio` = `dashu::ratio::RBig` = `dashu_ratio::RBig` -- `dashu::Real` = `dashu::float::FBig` = `dashu_float::FBig` -- `dashu::Decimal` = `dashu::float::DBig` = `dashu_float::DBig` +The crate `dashu` is a meta crate that exposes all the functionalities of the subcrates (`dashu-base`, `dashu-int`, `dashu-float`, `dashu-ratio`, `dashu-cmplx` and `dashu-macros`). Each subcrate becomes a module in `dashu`: `dashu-base` → `dashu::base`, `dashu-int` → `dashu::integer`, `dashu-float` → `dashu::float`, `dashu-ratio` → `dashu::rational`, `dashu-cmplx` → `dashu::complex`. It also creates more readable aliases for the numeric types: +- `dashu::Natural` = `dashu::integer::UBig` +- `dashu::Integer` = `dashu::integer::IBig` +- `dashu::Rational` = `dashu::rational::RBig` +- `dashu::Real` = `dashu::float::FBig` +- `dashu::Decimal` = `dashu::float::DBig` +- `dashu::Complex` = `dashu::complex::CBig` In this guide, we will use the original names of the numeric types (i.e. `XBig`), but the explanations are also applicable to these re-exported types. diff --git a/guide/src/io/interop.md b/guide/src/io/interop.md index 64a6ef95..272ba2ce 100644 --- a/guide/src/io/interop.md +++ b/guide/src/io/interop.md @@ -5,7 +5,7 @@ Besides the standard formatting and parsing traits, `dashu-int` exposes lower-le `UBig::to_digits(base)` returns the number's digits in any base `2..=Word::MAX` (most-significant first, stored as `Word`), and `UBig::from_digits(base, &digits)` reconstructs it. This generalizes `in_radix` (which is limited to base 2–36 for string output) to arbitrary bases and word-sized digits. ```rust -use dashu_int::UBig; +use dashu::integer::UBig; let n = UBig::from(0x1234u16); let digits = n.to_digits(16); // [1, 2, 3, 4], most-significant first @@ -21,7 +21,7 @@ assert_eq!(UBig::from_digits(16, &digits)?, n); `UBig::from_words(&[w0, w1, …])` builds a value from little-endian words, and `.as_words()` borrows the underlying word slice without copying. This is the closest to the raw in-memory form. ```rust -use dashu_int::{UBig, Word}; +use dashu::integer::{UBig, Word}; let n = UBig::from_words(&[3, 2, 1]); // 3 + 2·Word + 1·Word² let words: &[Word] = n.as_words(); diff --git a/guide/src/io/parse.md b/guide/src/io/parse.md index ee4f401f..1c25de3f 100644 --- a/guide/src/io/parse.md +++ b/guide/src/io/parse.md @@ -5,7 +5,7 @@ Every numeric type implements `FromStr`, so values can be built with `"...".pars `UBig::from_str` / `IBig::from_str` accept an optional sign followed by decimal digits. For other bases use `from_str_radix(s, radix)` (radix 2–36); it recognizes a `0x`/`0o`/`0b` prefix independently of the `radix` argument. ```rust -use dashu_int::{UBig, IBig}; +use dashu::integer::{UBig, IBig}; use core::str::FromStr; assert_eq!(UBig::from_str("12345")?, UBig::from(12345u16)); @@ -26,7 +26,7 @@ assert_eq!(IBig::from_str_radix("-1aff", 16)?, IBig::from(-0x1aff)); Precision is inferred from the number of significant digits presented. String `inf`/`NaN` literals are **not** accepted — construct infinities from the `INFINITY` constant instead. ```rust -use dashu_float::DBig; +use dashu::float::DBig; use core::str::FromStr; assert_eq!(format!("{:e}", DBig::from_str("6.022e23")?), "6.022e23"); @@ -38,7 +38,7 @@ assert_eq!(DBig::from_str("-0.0123456789")?.to_string(), "-0.0123456789"); `RBig::from_str` accepts `numerator/denominator`, or just a numerator (denominator defaults to 1). `from_str_radix` parses both parts in the given base; a `0x`/`0o`/`0b` prefix must be consistent between them. ```rust -use dashu_ratio::RBig; +use dashu::rational::RBig; use core::str::FromStr; assert_eq!(RBig::from_str("22/7")?.to_string(), "22/7"); @@ -46,11 +46,11 @@ assert_eq!(RBig::from_str("22/7")?.to_string(), "22/7"); ## Parsing Complex -`CBig` `FromStr` accepts the same algebraic $a+bi$ grammar that `Display` emits: an optional real term plus an optional signed imaginary term (at least one required); a unit coefficient may be omitted (`i`, `-i`). The MPC-style parenthesized form `(re im)` is **not** accepted. +`CBig::FromStr` accepts the same algebraic $a+bi$ grammar that `Display` emits: an optional real term plus an optional signed imaginary term (at least one required); a unit coefficient may be omitted (`i`, `-i`). The MPC-style parenthesized form `(re im)` is **not** accepted. ```rust -use dashu_cmplx::CBig; -use dashu_float::round::mode::HalfAway; +use dashu::complex::CBig; +use dashu::float::round::mode::HalfAway; use core::str::FromStr; type C = CBig; diff --git a/guide/src/io/print.md b/guide/src/io/print.md index 920b27e7..38313f4e 100644 --- a/guide/src/io/print.md +++ b/guide/src/io/print.md @@ -5,7 +5,7 @@ `Display` renders a `UBig`/`IBig` in decimal. The `Binary`, `Octal`, `LowerHex`, and `UpperHex` traits render in base 2/8/16, with the `#` flag adding the conventional `0b`/`0o`/`0x`/`0X` prefix. For any other radix, use `in_radix(r)` (base 2–36); its `#` flag uppercases digits above 9. ```rust -use dashu_int::UBig; +use dashu::integer::UBig; let n = UBig::from(255u8); assert_eq!(format!("{}", n), "255"); @@ -27,7 +27,7 @@ There are two forms, controlled by the formatter flags: Shows the truncated head‥tail representation. ```rust -use dashu_int::{UBig, IBig}; +use dashu::integer::{UBig, IBig}; // Small integers print in full assert_eq!(format!("{:?}", UBig::from(12345u16)), "12345"); @@ -51,7 +51,7 @@ The number of digits shown on each end depends on the `Word` size — on 64-bit Adds `(digits: N, bits: M)` after the head‥tail representation, showing the total digit count and bit length. ```rust -use dashu_int::{UBig, Word}; +use dashu::integer::{UBig, Word}; let x = UBig::ONE << 1000; if Word::BITS == 64 { @@ -67,7 +67,7 @@ if Word::BITS == 64 { `FBig`/`DBig` `Display` renders the significand with the radix point positioned by the exponent — the natural positional form, not scientific. The formatter precision option rounds to that many fractional digits. ```rust -use dashu_float::DBig; +use dashu::float::DBig; use core::str::FromStr; assert_eq!(format!("{}", DBig::from_str("12.34")?), "12.34"); @@ -77,7 +77,7 @@ assert_eq!(format!("{:.1}", DBig::from_str("12.34")?), "12.3"); For scientific notation use `LowerExp`/`UpperExp`: the exponent marker is `e`/`E` in base 10 and `@` in other bases. `Debug` prints `significand * base ^ exponent (prec: N)` (or a struct with `{:#?}`). Infinities render as `inf` / `-inf` under both `Display` and `Debug`. ```rust -use dashu_float::DBig; +use dashu::float::DBig; use core::str::FromStr; assert_eq!(format!("{:e}", DBig::from_str("1234.5")?), "1.2345e3"); @@ -89,7 +89,7 @@ assert_eq!(format!("{:E}", DBig::from_str("1234.5")?), "1.2345E3"); `RBig`/`Relaxed` `Display` renders as `numerator/denominator`, or just the numerator when the denominator is `1`. The `Binary`/`Octal`/`LowerHex`/`UpperHex` traits and `in_radix(r)` format both parts in the given base. ```rust -use dashu_ratio::RBig; +use dashu::rational::RBig; use core::str::FromStr; assert_eq!(format!("{}", RBig::from_str("22/7")?), "22/7"); @@ -99,7 +99,7 @@ assert_eq!(format!("{}", RBig::from_str("5/1")?), "5"); For the positional (decimal) expansion use `in_expanded()`. `{:.N}` prints exactly `N` fractional digits; the `#` flag detects the repeating part and parenthesizes it: ```rust -use dashu_ratio::RBig; +use dashu::rational::RBig; let x = RBig::from_parts(1.into(), 3u8.into()); assert_eq!(format!("{:.4}", x.in_expanded()), "0.3333"); @@ -111,8 +111,8 @@ assert_eq!(format!("{:#}", x.in_expanded()), "0.(3)"); `CBig` `Display` uses the algebraic $a+bi$ notation: the imaginary term always carries an explicit sign, a unit coefficient is elided (`i`, not `1i`), and a zero imaginary part is omitted. `Debug` prints `re: im: (prec:

)`. ```rust -use dashu_cmplx::CBig; -use dashu_float::{FBig, round::mode::HalfAway}; +use dashu::complex::CBig; +use dashu::float::{FBig, round::mode::HalfAway}; type C = CBig; type F = FBig; diff --git a/guide/src/io/serialize.md b/guide/src/io/serialize.md index 3a2f4951..fe86103c 100644 --- a/guide/src/io/serialize.md +++ b/guide/src/io/serialize.md @@ -9,7 +9,7 @@ dashu offers three layers of (de)serialization for its integer and float types, `UBig` and `IBig` convert to and from explicit-endianness byte sequences via `to_le_bytes` / `to_be_bytes` and `from_le_bytes` / `from_be_bytes`. These are portable, layout-stable formats suitable for binary interchange. ```rust -use dashu_int::UBig; +use dashu::integer::UBig; let n = UBig::from(0x12345678u32); let bytes = n.to_le_bytes(); diff --git a/guide/src/ops/basic.md b/guide/src/ops/basic.md index 8d3192b7..d6d04fab 100644 --- a/guide/src/ops/basic.md +++ b/guide/src/ops/basic.md @@ -5,7 +5,7 @@ The standard arithmetic operators are implemented for all numeric types, for bot `UBig` and `IBig` support `+`, `-`, `*`, `/`, and `%`. Integer division rounds toward zero, and the remainder takes the sign of the dividend (the C/Rust convention). For Euclidean division (non-negative remainder) use the `DivRemEuclid` / `RemEuclid` traits from `dashu-base`; `DivRem` returns both quotient and remainder at once. ```rust -use dashu_int::IBig; +use dashu::integer::IBig; let b = IBig::from(-0x10ff); let e = 2 * &b - 1; // mixes naturally with primitives @@ -25,8 +25,8 @@ assert_eq!(e, IBig::from(-0x21ff)); `CBig` supports the field operations `+`, `-`, `*`, `/`, plus `sqr` and `inv` (multiplicative inverse). Multiplication and division by a real `FBig` are also available as mixed-type operators. Multiplication and division use Smith's method with a guard digit and re-round, giving the same near-correctly-rounded guarantee as `dashu-float`'s transcendentals. ```rust -use dashu_cmplx::CBig; -use dashu_float::{FBig, round::mode::HalfAway}; +use dashu::complex::CBig; +use dashu::float::{FBig, round::mode::HalfAway}; type C = CBig; let z = C::from_parts(FBig::from(3), FBig::from(4)); diff --git a/guide/src/ops/bit.md b/guide/src/ops/bit.md index b521be04..6303efe8 100644 --- a/guide/src/ops/bit.md +++ b/guide/src/ops/bit.md @@ -1,7 +1,7 @@ `UBig` and `IBig` support the bitwise operators `&` (and), `|` (or), `^` (xor), and `!` (not). On `UBig`, `!` is an *infinite-width* complement — every bit above the highest set bit is treated as `1`, so `!n` is generally a very large number. On `IBig`, `!` follows the two's-complement rule. ```rust -use dashu_int::UBig; +use dashu::integer::UBig; let a = UBig::from(0b1100u8); let b = UBig::from(0b1010u8); @@ -22,8 +22,8 @@ The `BitTest` trait (from `dashu-base`) tests and measures individual bits: `.bi Because a `UBig` has unbounded width, it works naturally as an arbitrarily large bit set: set bit `i` with `set_bit(i)`, test it with `bit(i)`, and read the extent with `bit_len()`. ```rust -use dashu_base::BitTest; -use dashu_int::UBig; +use dashu::base::BitTest; +use dashu::integer::UBig; let mut bits = UBig::ZERO; bits.set_bit(0); diff --git a/guide/src/ops/cmp.md b/guide/src/ops/cmp.md index f450bed4..aa743f8e 100644 --- a/guide/src/ops/cmp.md +++ b/guide/src/ops/cmp.md @@ -10,7 +10,7 @@ Comparison is natively enabled **only between big numbers of the same kind**, no ## Sign -The signed types (`IBig`, `FBig`/`DBig`, `RBig`, `CBig`) expose `.sign()` (returning `dashu_base::Sign`, where zero is `Positive`) and `.signum()` (returning `-1`, `0`, or `+1` as the same type). +The signed types (`IBig`, `FBig`/`DBig`, `RBig`, `CBig`) expose `.sign()` (returning `dashu::base::Sign`, where zero is `Positive`) and `.signum()` (returning `-1`, `0`, or `+1` as the same type). ## Magnitude comparison and cross-type ordering diff --git a/guide/src/ops/num_theory.md b/guide/src/ops/num_theory.md index a1b4fef9..c42988a4 100644 --- a/guide/src/ops/num_theory.md +++ b/guide/src/ops/num_theory.md @@ -5,8 +5,8 @@ The `Gcd` trait (from `dashu-base`) gives `gcd`, and `ExtendedGcd` gives `gcd_ext`, which returns `(gcd, x, y)` with $a\cdot x + b\cdot y = \gcd(a,b)$. ```rust -use dashu_base::Gcd; -use dashu_int::UBig; +use dashu::base::Gcd; +use dashu::integer::UBig; let a = UBig::from(12u8); let b = UBig::from(8u8); @@ -18,7 +18,7 @@ assert_eq!((&a).gcd(&b), UBig::from(4u8)); For repeated operations against a fixed modulus, precompute a `ConstDivisor` and reduce values into `Reduced`. Addition, subtraction, multiplication, exponentiation, and inversion then run against the precomputed modulus, and the result prints in `(mod N)` form. ```rust -use dashu_int::{UBig, fast_div::ConstDivisor}; +use dashu::integer::{UBig, fast_div::ConstDivisor}; let ring = ConstDivisor::new(UBig::from(10000u32)); let x = ring.reduce(12345); diff --git a/guide/src/types.md b/guide/src/types.md index a5b96f60..c6874cb2 100644 --- a/guide/src/types.md +++ b/guide/src/types.md @@ -2,26 +2,26 @@ In `dashu` crates, there are standalone types for each kind of numbers with arbitrary precision, as listed below: -- `dashu_int::UBig` (alias `dashu::Natural`) represents unsigned integers (i.e. natural numbers). -- `dashu_int::IBig` (alias `dashu::Integer`) represents (signed) integers. -- `dashu_float::FBig` (alias `dashu::Real`) represents real numbers with floating point representation ($\text{significand} \times \text{base}^{\text{exponent}}$) -- `dashu_float::DBig` (alias `dashu::Decimal`) is a specialization of `FBig` with `base = 10`. -- `dashu_ratio::RBig` (alias `dashu::Rational`) represents rational numbers. It has a variant `dashu_ratio::Relaxed`, which also represents a rational number, but it doesn't enforce that the number is in the canonicalized form. -- `dashu_cmplx::CBig` (alias `dashu::Complex`) represents complex numbers, built as a pair of `FBig` parts sharing one precision and rounding mode. +- `dashu::integer::UBig` (alias `dashu::Natural`) represents unsigned integers (i.e. natural numbers). +- `dashu::integer::IBig` (alias `dashu::Integer`) represents (signed) integers. +- `dashu::float::FBig` (alias `dashu::Real`) represents real numbers with floating point representation ($\text{significand} \times \text{base}^{\text{exponent}}$) +- `dashu::float::DBig` (alias `dashu::Decimal`) is a specialization of `FBig` with `base = 10`. +- `dashu::rational::RBig` (alias `dashu::Rational`) represents rational numbers. It has a variant `dashu::rational::Relaxed`, which also represents a rational number, but it doesn't enforce that the number is in the canonicalized form. +- `dashu::complex::CBig` (alias `dashu::Complex`) represents complex numbers, built as a pair of `FBig` parts sharing one precision and rounding mode. Common operations are implemented for all these numeric types, please refer to the other sections or the API docs for the usages. ### Word -A `dashu_int::Word` is an unsigned integer representing a native machine word. The size of a `Word` usually depends on the platform, for example, the `Word` is `u32` on 32-bit platforms. However, the behavior can be overriden by setting the `force_bits` config flag (e.g. add `--cfg force_bits="32"` to the environment variable `RUSTFLAGS`). Since this type is not consistant across platforms, be careful to use it when writing portable programs. +A `dashu::integer::Word` is an unsigned integer representing a native machine word. The size of a `Word` usually depends on the platform, for example, the `Word` is `u32` on 32-bit platforms. However, the behavior can be overriden by setting the `force_bits` config flag (e.g. add `--cfg force_bits="32"` to the environment variable `RUSTFLAGS`). Since this type is not consistant across platforms, be careful to use it when writing portable programs. Moreover, there is another type `DoubleWord` representing an integer type with double the size of a `Word`. It's the maximum integer type that can fit in a `UBig` instance without heap allocation. It's also involved in some const constructors. ### Sign -A `dashu_base::Sign` is a **binary** enum to represent the sign of numbers. Due to effciency and clarity, the number zero will be categorized as `Sign::Positive`, even though it's mathematically unsigned. (Imagine if you store the sign in a ternary format, every number instance will have to pay an extra bit to store the sign, and extra branches to do operations.) To get a ternary representation, it's recommended to use the `.signum()` methods on the numeric types. +A `dashu::base::Sign` is a **binary** enum to represent the sign of numbers. Due to effciency and clarity, the number zero will be categorized as `Sign::Positive`, even though it's mathematically unsigned. (Imagine if you store the sign in a ternary format, every number instance will have to pay an extra bit to store the sign, and extra branches to do operations.) To get a ternary representation, it's recommended to use the `.signum()` methods on the numeric types. -Convenient utilities related to the sign are provided with this enum. For example, you can get the sign of any primitive numbers or big numbers through the `dashu_base::Signed` trait, you can also multiply the sign by another sign. You can even multiply the sign with `core::cmp::Ordering`, this is very handy when you want to flip a comparison result based on the sign of operands, and this is widely used in the comparison implementations in `dashu`. +Convenient utilities related to the sign are provided with this enum. For example, you can get the sign of any primitive numbers or big numbers through the `dashu::base::Signed` trait, you can also multiply the sign by another sign. You can even multiply the sign with `core::cmp::Ordering`, this is very handy when you want to flip a comparison result based on the sign of operands, and this is widely used in the comparison implementations in `dashu`. ### Layout of `UBig` @@ -31,7 +31,7 @@ The most fundamental type of the `dashu` libraries is the natural number `UBig`. ### Layout of `FBig` -The layout of `FBig` (and `DBig`) is a little different from other types. An `FBig` instance contains a number representation `dashu_float::Repr` and a context `dashu_float::Context`. The context will be copied every time a new `FBig` is created based on it. The context currently contains the rounding information and the precision associated with this number. The context is kept deliberately lightweight (`Copy` + `Send` + `Sync`): the shared cache for math constants (such as π, ln2, ln10) lives *outside* the context, in the separate [`CachedFBig`](./cached.md) wrapper, so that a plain `FBig` stays cheap to copy and usable in `const`/`static` contexts. Therefore, if you don't want to store the additional context information, you can just store the `Repr` part of the `FBig`. The later operations on the `Repr` can be called with the associated methods of the `Context`, which all takes the reference to a `Repr` instance. However, this could lead to a little overhead in some cases. +The layout of `FBig` (and `DBig`) is a little different from other types. An `FBig` instance contains a number representation `dashu::float::Repr` and a context `dashu::float::Context`. The context will be copied every time a new `FBig` is created based on it. The context currently contains the rounding information and the precision associated with this number. The context is kept deliberately lightweight (`Copy` + `Send` + `Sync`): the shared cache for math constants (such as π, ln2, ln10) lives *outside* the context, in the separate [`CachedFBig`](./cached.md) wrapper, so that a plain `FBig` stays cheap to copy and usable in `const`/`static` contexts. Therefore, if you don't want to store the additional context information, you can just store the `Repr` part of the `FBig`. The later operations on the `Repr` can be called with the associated methods of the `Context`, which all takes the reference to a `Repr` instance. However, this could lead to a little overhead in some cases. ### Layout of `CBig` @@ -45,9 +45,9 @@ Besides the numeric types, there are several auxiliary types used across the cra ### Sign -In `dashu`, the sign of the numbers are represented as an enum `dashu_base::Sign`. It only has two variants: `Positive` and `Negative`. Zero is considered as `Positive`. A `Sign` can be converted from a boolean value using `::from()`, where `true` is mapped to `Negative`. +In `dashu`, the sign of the numbers are represented as an enum `dashu::base::Sign`. It only has two variants: `Positive` and `Negative`. Zero is considered as `Positive`. A `Sign` can be converted from a boolean value using `::from()`, where `true` is mapped to `Negative`. -To get the sign of a number, usually there is a `.sign()` method for the numeric types. For primitive integers, the sign can be retrieved with the `dashu_base::Signed` trait. +To get the sign of a number, usually there is a `.sign()` method for the numeric types. For primitive integers, the sign can be retrieved with the `dashu::base::Signed` trait. The type `Sign` also supports some operations, namely `Neg` and `Mul`. The sign can be flipped using `Neg` and it can be multiplied with another `Sign` or other numeric types to their signs. @@ -59,8 +59,8 @@ When you have an `Approximation` instance, call `.value()`, `.value_ref()` or `u ### ConstCache -`dashu_float::ConstCache` holds the exact binary-splitting state for the mathematical constants π, ln2, and ln10, so repeated transcendental calls at increasing precision *extend* prior work instead of recomputing from scratch. It is a plain struct of big integers — base-free, `Send` + `Sync` — and a single cache serves any base. `FBig` and `Context` themselves stay `Copy` and carry no cache; the state lives in the separate [`CachedFBig`](./cached.md) wrapper (as `Rc>`), or you can drive a bare `ConstCache` directly. +`dashu::float::ConstCache` holds the exact binary-splitting state for the mathematical constants π, ln2, and ln10, so repeated transcendental calls at increasing precision *extend* prior work instead of recomputing from scratch. It is a plain struct of big integers — base-free, `Send` + `Sync` — and a single cache serves any base. `FBig` and `Context` themselves stay `Copy` and carry no cache; the state lives in the separate [`CachedFBig`](./cached.md) wrapper (as `Rc>`), or you can drive a bare `ConstCache` directly. ### FpResult and CfpResult -Inexact operations at the context layer return a result type rather than a bare value: `dashu_float::FpResult = Result, FpError>`, where `Rounded` is the [`Approximation`](#approximation) carrying a `Rounding` flag. The complex analog is `dashu_cmplx::CfpResult` (`Result, FpError>`), whose `CRounded` carries one `Rounding` flag per axis. `FpError` reports why an operation could not produce a finite correctly-rounded value: `Overflow`/`Underflow` (saturated to `±∞`/`±0` by the convenience layer), `Indeterminate` (e.g. `0/0`), `OutOfDomain`, and `InfiniteInput`. The convenience-layer methods unwrap these — saturating overflow/underflow and panicking on the rest. +Inexact operations at the context layer return a result type rather than a bare value: `dashu::float::FpResult = Result, FpError>`, where `Rounded` is the [`Approximation`](#approximation) carrying a `Rounding` flag. The complex analog is `dashu::complex::CfpResult` (`Result, FpError>`), whose `CRounded` carries one `Rounding` flag per axis. `FpError` reports why an operation could not produce a finite correctly-rounded value: `Overflow`/`Underflow` (saturated to `±∞`/`±0` by the convenience layer), `Indeterminate` (e.g. `0/0`), `OutOfDomain`, and `InfiniteInput`. The convenience-layer methods unwrap these — saturating overflow/underflow and panicking on the rest. From ac7e55225de3bd9a29c288561b25a80b114e492b Mon Sep 17 00:00:00 2001 From: Jacob Zhong Date: Mon, 6 Jul 2026 18:33:37 +0800 Subject: [PATCH 18/21] Guide/print: move Debug Print to the end; show every type's debug output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the Debug Print section to the end of the Printing page (after the Display sections) and replace the integer-only examples with one comprehensive block showing the {:?} output of every numeric kind — UBig/IBig, FBig/DBig, CachedFBig, RBig, CBig — with each string verified against the actual implementation. Co-Authored-By: Claude --- guide/src/io/print.md | 109 ++++++++++++++++++++++-------------------- 1 file changed, 58 insertions(+), 51 deletions(-) diff --git a/guide/src/io/print.md b/guide/src/io/print.md index 38313f4e..903628f1 100644 --- a/guide/src/io/print.md +++ b/guide/src/io/print.md @@ -16,69 +16,23 @@ assert_eq!(format!("{}", n.in_radix(16)), "ff"); assert_eq!(format!("{:#}", n.in_radix(16)), "FF"); ``` -## Debug Print - -The `Debug` implementation uses a compact **head‥tail** format for large integers: it prints the most significant digits, a `..` separator, and the least significant digits, omitting the middle. For small integers that fit in a single `Word` or `DoubleWord` the full number is shown without truncation. - -There are two forms, controlled by the formatter flags: - -### Simple form (`{:?}`) - -Shows the truncated head‥tail representation. - -```rust -use dashu::integer::{UBig, IBig}; - -// Small integers print in full -assert_eq!(format!("{:?}", UBig::from(12345u16)), "12345"); -assert_eq!(format!("{:?}", IBig::from(-12345)), "-12345"); - -// Large integers show head..tail (example for 64-bit Word) -assert_eq!( - format!("{:?}", UBig::ONE << 1000), - "1071508607186267320..4386837205668069376" -); -assert_eq!( - format!("{:?}", IBig::NEG_ONE << 1000), - "-1071508607186267320..4386837205668069376" -); -``` - -The number of digits shown on each end depends on the `Word` size — on 64-bit targets it is 19 decimal digits at each end (one word's worth), on 32-bit targets it is 9 digits. - -### Verbose form (`{:#?}`) - -Adds `(digits: N, bits: M)` after the head‥tail representation, showing the total digit count and bit length. - -```rust -use dashu::integer::{UBig, Word}; - -let x = UBig::ONE << 1000; -if Word::BITS == 64 { - assert_eq!( - format!("{:#?}", x), - "1071508607186267320..4386837205668069376 (digits: 302, bits: 1001)" - ); -} -``` - ## Float Formatting `FBig`/`DBig` `Display` renders the significand with the radix point positioned by the exponent — the natural positional form, not scientific. The formatter precision option rounds to that many fractional digits. ```rust -use dashu::float::DBig; use core::str::FromStr; +use dashu::float::DBig; assert_eq!(format!("{}", DBig::from_str("12.34")?), "12.34"); assert_eq!(format!("{:.1}", DBig::from_str("12.34")?), "12.3"); ``` -For scientific notation use `LowerExp`/`UpperExp`: the exponent marker is `e`/`E` in base 10 and `@` in other bases. `Debug` prints `significand * base ^ exponent (prec: N)` (or a struct with `{:#?}`). Infinities render as `inf` / `-inf` under both `Display` and `Debug`. +For scientific notation use `LowerExp`/`UpperExp`: the exponent marker is `e`/`E` in base 10 and `@` in other bases. Infinities render as `inf` / `-inf` under both `Display` and `Debug`. ```rust -use dashu::float::DBig; use core::str::FromStr; +use dashu::float::DBig; assert_eq!(format!("{:e}", DBig::from_str("1234.5")?), "1.2345e3"); assert_eq!(format!("{:E}", DBig::from_str("1234.5")?), "1.2345E3"); @@ -89,8 +43,8 @@ assert_eq!(format!("{:E}", DBig::from_str("1234.5")?), "1.2345E3"); `RBig`/`Relaxed` `Display` renders as `numerator/denominator`, or just the numerator when the denominator is `1`. The `Binary`/`Octal`/`LowerHex`/`UpperHex` traits and `in_radix(r)` format both parts in the given base. ```rust -use dashu::rational::RBig; use core::str::FromStr; +use dashu::rational::RBig; assert_eq!(format!("{}", RBig::from_str("22/7")?), "22/7"); assert_eq!(format!("{}", RBig::from_str("5/1")?), "5"); @@ -108,7 +62,7 @@ assert_eq!(format!("{:#}", x.in_expanded()), "0.(3)"); ## Complex Formatting -`CBig` `Display` uses the algebraic $a+bi$ notation: the imaginary term always carries an explicit sign, a unit coefficient is elided (`i`, not `1i`), and a zero imaginary part is omitted. `Debug` prints `re: im: (prec:

)`. +`CBig` `Display` uses the algebraic $a+bi$ notation: the imaginary term always carries an explicit sign, a unit coefficient is elided (`i`, not `1i`), and a zero imaginary part is omitted. ```rust use dashu::complex::CBig; @@ -125,3 +79,56 @@ assert_eq!(format!("{}", C::from_parts(F::from(0), F::from(-1))), "-i"); ``` The same algebraic grammar is accepted on input — see [Parsing](./parse.md). + +## Debug Print + +`Debug` output is meant for quick inspection (it is **not** a stable serialization format — see [Serialization](./serialize.md)). Large integers use a compact **head‥tail** format — the most-significant digits, a `..` separator, then the least-significant digits, with the middle omitted — while small integers print in full. Each numeric type has its own `Debug` shape: + +```rust +use core::str::FromStr; +use dashu::complex::CBig; +use dashu::float::{CachedFBig, Context, DBig, FBig, Repr, round::mode::HalfAway}; +use dashu::integer::{IBig, UBig}; +use dashu::rational::RBig; + +// UBig / IBig — head..tail for large values, full for small +assert_eq!(format!("{:?}", UBig::from(12345u16)), "12345"); +assert_eq!(format!("{:?}", IBig::from(-12345)), "-12345"); +assert_eq!( + format!("{:?}", UBig::ONE << 1000), + "1071508607186267320..4386837205668069376" +); + +// FBig / DBig — significand * base ^ exponent (prec: N) +let f: FBig = FBig::from(3u8); // FBig +assert_eq!(format!("{:?}", f), "3 * 2 ^ 0 (prec: 2)"); +assert_eq!(format!("{:?}", DBig::from_str("12.34")?), "1234 * 10 ^ -2 (prec: 4)"); + +// CachedFBig — a struct exposing the repr and precision +let c = CachedFBig::::with_cache(Repr::new(1234.into(), -3), Context::new(50)); +assert_eq!(format!("{:?}", c), "CachedFBig { repr: 1234 * 10 ^ -3, precision: 50 }"); + +// RBig — numerator / denominator +assert_eq!(format!("{:?}", RBig::from_parts(1.into(), 3u8.into())), "1 / 3"); + +// CBig — re: im: (prec: N) +type F = FBig; +assert_eq!( + format!("{:?}", CBig::::from_parts(F::from(3), F::from(4))), + "re:3 im:4 (prec: 1)" +); +``` + +The head‥tail digit count depends on the `Word` size — 19 decimal digits per end on 64-bit targets, 9 on 32-bit. The verbose form `{:#?}` adds detail; for integers it appends the digit count and bit length: + +```rust +use dashu::integer::{UBig, Word}; + +let x = UBig::ONE << 1000; +if Word::BITS == 64 { + assert_eq!( + format!("{:#?}", x), + "1071508607186267320..4386837205668069376 (digits: 302, bits: 1001)" + ); +} +``` From 7250fce079bdb45edb427d4772fc5a92a10e1dd9 Mon Sep 17 00:00:00 2001 From: Jacob Zhong Date: Mon, 6 Jul 2026 20:21:35 +0800 Subject: [PATCH 19/21] Guide/print: show {:#?} output for every numeric type Replace the integer-only verbose-form example with one that pretty-prints every kind (UBig, FBig, DBig, CachedFBig, RBig, CBig), each output verified against the implementation. Output shown via a text block since {:#?} is multi-line. Co-Authored-By: Claude --- guide/src/io/print.md | 70 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 9 deletions(-) diff --git a/guide/src/io/print.md b/guide/src/io/print.md index 903628f1..e6a627fc 100644 --- a/guide/src/io/print.md +++ b/guide/src/io/print.md @@ -119,16 +119,68 @@ assert_eq!( ); ``` -The head‥tail digit count depends on the `Word` size — 19 decimal digits per end on 64-bit targets, 9 on 32-bit. The verbose form `{:#?}` adds detail; for integers it appends the digit count and bit length: +The head‥tail digit count depends on the `Word` size — 19 decimal digits per end on 64-bit targets, 9 on 32-bit. The verbose form `{:#?}` pretty-prints a structured view of the value; for the compound types it shows the full decomposition: ```rust -use dashu::integer::{UBig, Word}; - -let x = UBig::ONE << 1000; -if Word::BITS == 64 { - assert_eq!( - format!("{:#?}", x), - "1071508607186267320..4386837205668069376 (digits: 302, bits: 1001)" - ); +use core::str::FromStr; +use dashu::complex::CBig; +use dashu::float::{CachedFBig, Context, DBig, FBig, Repr, round::mode::HalfAway}; +use dashu::integer::UBig; +use dashu::rational::RBig; + +let f: FBig = FBig::from(3u8); +let c = CachedFBig::::with_cache(Repr::new(1234.into(), -3), Context::new(50)); +type F = FBig; +let z = CBig::::from_parts(F::from(3), F::from(4)); + +println!("{:#?}", UBig::from(12345u16)); +println!("{:#?}", f); +println!("{:#?}", DBig::from_str("12.34")?); +println!("{:#?}", c); +println!("{:#?}", RBig::from_parts(1.into(), 3u8.into())); +println!("{:#?}", z); +``` + +This prints, for `UBig`, `FBig`, `DBig`, `CachedFBig`, `RBig`, `CBig` in order: + +```text +12345 (digits: 5, bits: 14) +FBig { + significand: 3 (2 bits), + exponent: 2 ^ 0, + precision: 2, + rounding: Zero, +} +FBig { + significand: 1234 (digits: 4, bits: 11), + exponent: 10 ^ -2, + precision: 4, + rounding: HalfAway, +} +CachedFBig { + repr: Repr { + significand: 1234 (digits: 4, bits: 11), + exponent: 10 ^ -3, + }, + precision: 50, +} +RBig { + numerator: 1 (digits: 1, bits: 1), + denominator: 3 (digits: 1, bits: 2), +} +CBig { + re: FBig { + significand: 3 (digits: 1, bits: 2), + exponent: 10 ^ 0, + precision: 1, + rounding: HalfAway, + }, + im: FBig { + significand: 4 (digits: 1, bits: 3), + exponent: 10 ^ 0, + precision: 1, + rounding: HalfAway, + }, + precision: 1, } ``` From ced7f09a841e27a43d7602f3db31d2df63f7c9a7 Mon Sep 17 00:00:00 2001 From: Jacob Zhong Date: Mon, 6 Jul 2026 20:24:12 +0800 Subject: [PATCH 20/21] Guide: move "Two-layer API" from exp_log to types The two-layer (Context vs convenience) design applies to all inexact operations, not just exp/log/pow, so move it from the Exponential and Logarithm page to types.md as a general design section (and trim the now- redundant convenience-layer sentence from the FpResult subsection). exp_log.md keeps a one-line forward-reference. Co-Authored-By: Claude --- guide/src/ops/exp_log.md | 9 +-------- guide/src/types.md | 11 ++++++++++- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/guide/src/ops/exp_log.md b/guide/src/ops/exp_log.md index b9e1e16b..d2f63883 100644 --- a/guide/src/ops/exp_log.md +++ b/guide/src/ops/exp_log.md @@ -1,11 +1,4 @@ -`FBig`/`DBig` provide the exponential, logarithmic, power, and root families, plus the mathematical constants. `CBig` provides the complex analogs of each. - -## Two-layer API - -Like all inexact operations, transcendentals come in two layers (see [types](../types.md)): - -- **Context layer** — `Context` methods take a `&Repr` and return `FpResult>` (a correctly-rounded result or an `FpError`), carrying the rounding direction. They accept an optional `&mut ConstCache` for constant reuse. -- **Convenience layer** — methods on `FBig` (`.exp()`, `.ln()`, …) unwrap to a plain `FBig`, panicking on `Indeterminate`/`OutOfDomain`/`InfiniteInput` and saturating overflow/underflow to `±∞`/`±0`. +`FBig`/`DBig` provide the exponential, logarithmic, power, and root families, plus the mathematical constants. `CBig` provides the complex analogs of each. Like all inexact operations, these come in [two layers](../types.md#two-layer-api) — a `Context` layer that returns the rounding result and a convenience layer that unwraps it. ## Real functions diff --git a/guide/src/types.md b/guide/src/types.md index c6874cb2..883f226c 100644 --- a/guide/src/types.md +++ b/guide/src/types.md @@ -63,4 +63,13 @@ When you have an `Approximation` instance, call `.value()`, `.value_ref()` or `u ### FpResult and CfpResult -Inexact operations at the context layer return a result type rather than a bare value: `dashu::float::FpResult = Result, FpError>`, where `Rounded` is the [`Approximation`](#approximation) carrying a `Rounding` flag. The complex analog is `dashu::complex::CfpResult` (`Result, FpError>`), whose `CRounded` carries one `Rounding` flag per axis. `FpError` reports why an operation could not produce a finite correctly-rounded value: `Overflow`/`Underflow` (saturated to `±∞`/`±0` by the convenience layer), `Indeterminate` (e.g. `0/0`), `OutOfDomain`, and `InfiniteInput`. The convenience-layer methods unwrap these — saturating overflow/underflow and panicking on the rest. +Inexact operations at the context layer return a result type rather than a bare value: `dashu::float::FpResult = Result, FpError>`, where `Rounded` is the [`Approximation`](#approximation) carrying a `Rounding` flag. The complex analog is `dashu::complex::CfpResult` (`Result, FpError>`), whose `CRounded` carries one `Rounding` flag per axis. `FpError` reports why an operation could not produce a finite correctly-rounded value: `Overflow`/`Underflow`, `Indeterminate` (e.g. `0/0`), `OutOfDomain`, and `InfiniteInput`. + +## Two-layer API + +Inexact operations — division, the transcendentals, and anything else that can overflow, underflow, or be out of domain — are exposed in two layers: + +- **Context layer** — `Context` methods take a `&Repr` and return [`FpResult`](#fpresult-and-cfpresult)`>` (or `CfpResult>` for complex): a correctly-rounded result or an `FpError`, carrying the rounding direction. They accept an optional `&mut ConstCache` so constants can be reused. +- **Convenience layer** — the inherent methods and operators on `FBig`/`CBig` (`.exp()`, `.ln()`, `+`, `*`, …) unwrap to a plain value, panicking on `Indeterminate`/`OutOfDomain`/`InfiniteInput` and saturating overflow/underflow to `±∞`/`±0`. + +Use the convenience layer for everyday code; drop down to the `Context` layer when you need the rounding direction or explicit error handling. From f3933c3109955aab0a5bac9538535d5daea6c63f Mon Sep 17 00:00:00 2001 From: Jacob Zhong Date: Mon, 6 Jul 2026 20:27:59 +0800 Subject: [PATCH 21/21] Guide/num_theory: document the Montgomery reducer Add a "Montgomery reducer" section covering MontgomeryRepr/Montgomery (Montgomery-form modular arithmetic for odd moduli), with the Montgomery-vs-Barrett (Reduced) trade-off guidance and a worked example (Fermat's little theorem on a Mersenne prime) adapted from the crate's verified doctest. Co-Authored-By: Claude --- guide/src/ops/num_theory.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/guide/src/ops/num_theory.md b/guide/src/ops/num_theory.md index c42988a4..c9c12aec 100644 --- a/guide/src/ops/num_theory.md +++ b/guide/src/ops/num_theory.md @@ -26,6 +26,28 @@ let y = ring.reduce(55443); assert_eq!(format!("{}", x - y), "6902 (mod 10000)"); ``` +## Montgomery reducer + +For **odd moduli**, `MontgomeryRepr` offers [Montgomery-form] modular arithmetic — an alternative to the Barrett-style `ConstDivisor`/`Reduced` above. A modular multiplication is an ordinary multiplication followed by a Montgomery reduction (REDC) instead of a division, so it is faster than Barrett whenever the REDC is cheaper than the division. + +Montgomery multiplication, squaring, and exponentiation beat `Reduced` across roughly the 256–4096-bit range; beyond ~8 kbits the two are comparable. **For inverse-heavy workloads, prefer `Reduced`** — a Montgomery inverse must exit Montgomery form, run the extended GCD, and re-enter, whereas `Reduced` inverts directly. + +```rust +use dashu::integer::{UBig, monty::MontgomeryRepr}; + +// A Mersenne prime (odd). +let p = UBig::from(2u8).pow(607) - UBig::ONE; +let ring = MontgomeryRepr::new(p.clone()); + +// reduce values into Montgomery form, then multiply / square / pow modularly +let a = ring.reduce(123); +assert_eq!(a.pow(&(p - UBig::ONE)), ring.reduce(1)); // Fermat: a^(p-1) = 1 (mod p) +``` + +`MontgomeryRepr::new(m)` requires `m` to be odd; `ring.reduce(x)` lifts `x` into Montgomery form, and the resulting `Montgomery` values support `+`, `-`, `*`, `.sqr()`, `.pow(&exp)`, and `.inv()`. + +[Montgomery-form]: https://en.wikipedia.org/wiki/Montgomery_modular_multiplication + ## Diophantine approximation Rational approximation of reals — the simplest rational within a tolerance, continued fractions — lives on `RBig`; see [Conversion](../convert.md#conversion-to-rbig) for `simplest_in` / `nearest_in`.