# Getting started with zenith-float
This is a short path from a new crate dependency to a computed, formatted result. For a longer explanation (precision, rounding, recipes, mistakes), see [HELP.md](HELP.md). For every public name, see [LIBRARY.md](LIBRARY.md). For how `expr!` rounds, see [EXPR.md](EXPR.md).
All arithmetic is software integer limbs. There are no hardware binary interchange types in this crate.
## 1. Depend on the crate
```toml
[dependencies]
zenith-float = "1.0"
```
`std` is on by default (formatting and `FromStr`). For `no_std` plus an allocator:
```toml
zenith-float = { version = "1.0", default-features = false }
```
## 2. Construct numbers
Precision is a bit count, rounded up to the word size (64 bits on 64-bit targets). Rounding is explicit on almost every operation.
```rust
use zenith_float::{exact, Consts, ExactNum, RoundingMode};
let p = 256;
let rm = RoundingMode::ToEven;
let mut cc = Consts::new().expect("constants cache");
let a = ExactNum::from_u32(3, p);
let b = ExactNum::parse("0.5", zenith_float::Radix::Dec, p, rm, &mut cc);
let c = a.add(&b, p, rm); // 3.5
let x = exact!("1.25");
```
`parse` returns `ExactNum`, not `Result`. A failed parse is `NaN`; `ExactNum::err()` recovers the `Error`. `from_u32` is one of `from_u8` / `from_u16` / `from_u32` / `from_u64` / `from_u128` (and the signed `from_i*` counterparts). Compile-time decimals: `exact!("…")` or the alias `fbig!("…")`. You can also write `zenith_float::exact!("1.25")` without importing the macro.
Infinities and NaN are software values: `INF_POS`, `INF_NEG`, `NAN`. Errors (overflow, division by zero, bad arguments, allocation) become NaN; `ExactNum::err()` recovers the `Error`.
## 3. Wire a `Context` and use `expr!`
`expr!` needs a context: precision, rounding mode, a `Consts` cache, and an exponent range. Use the **narrowest** exponent range that still holds your results; working precision can grow with the exponent.
```rust
use zenith_float::{expr, Consts, RoundingMode};
use zenith_float::ctx::Context;
let mut ctx = Context::new(
256,
RoundingMode::ToEven,
Consts::new().expect("constants cache"),
-10_000,
10_000,
);
let y = expr!(sin(pi / 6), &mut ctx);
```
A temporary tuple works too: `(p, rm, &mut cc)` or `(p, rm, &mut cc, emin, emax)`.
`expr!` raises working precision to fight cancellation. It does **not** by itself guarantee a correctly rounded final bit pattern; see [EXPR.md](EXPR.md).
To evaluate a sub-expression with a different rounding mode:
```rust
The previous mode is restored when the closure returns.
## 4. Format output
`y` below is an owned `ExactNum` after `expr!` returns, so the `&mut ctx` borrow from the macro has ended. `format` takes `&self` and `&mut Consts`; `ctx.consts()` is that cache. `format` returns `Result<String, Error>` (unlike `parse`).
```rust
use zenith_float::{expr, Consts, RoundingMode};
use zenith_float::ctx::Context;
let mut ctx = Context::new(
256,
RoundingMode::ToEven,
Consts::new().expect("constants cache"),
-10_000,
10_000,
);
let y = expr!(sin(pi / 6), &mut ctx);
println!("{}", y);
println!("{:e}", y);
let s = y
.format(zenith_float::Radix::Hex, RoundingMode::ToEven, ctx.consts())
.expect("format");
```
## 5. When to call methods instead of `expr!`
Use `ExactNum` methods when you need paired results (`sin_cos`, `sinh_cosh`), IEEE-style split (`frexp`, `ilogb`), or compensated primitives (`two_sum`, `two_product`, `fused_sum`, `fused_dot`, `polyval`).
`fused_sum` / `fused_dot` add (or multiply-then-add) at extra working precision and round **once**. `polyval` is Horner with **lowest degree first**: `coeffs[0]` is `a0` in `a0 + a1 x + a2 x² + …`. `two_sum` / `two_product` compute the exact limb sum or product, then round that value to `p` bits with `rm` to get `hi`; `lo` is the remainder. Reconstruct with `hi.add(&lo, p, rm)` (not a hardware-float Dekker two-sum, which has no `p`/`rm`).
```rust
use zenith_float::{ExactNum, RoundingMode};
let p = 256;
let rm = RoundingMode::ToEven;
let one = ExactNum::from_u32(1, p);
let two = ExactNum::from_u32(2, p);
let three = ExactNum::from_u32(3, p);
let six = ExactNum::fused_sum(&[one.clone(), two.clone(), three.clone()], p, rm);
// polyval: [a0, a1, a2] = 1 + 2x + 3x²; at x = 2 that is 17
let seventeen = ExactNum::polyval(&[one.clone(), two.clone(), three.clone()], &two, p, rm);
let (hi, lo) = one.two_sum(&two, p, rm); // hi is 1+2 rounded to p bits
let three_again = hi.add(&lo, p, rm);
```
For **complex** expressions use `cexpr!` (documented with `expr!` in [LIBRARY.md](LIBRARY.md) §17). Same extra-precision loop; cancellation on both parts; imaginary unit `I`. `expr!` stays real-valued.
```rust
use zenith_float::{cexpr, Consts, RoundingMode};
use zenith_float::ctx::Context;
let mut ctx = Context::new(
256,
RoundingMode::ToEven,
Consts::new().expect("constants cache"),
-10_000,
10_000,
);
let z = cexpr!(I * I, &mut ctx); // −1 + 0i
```
Hardware IEEE registers are not used. For a fixed-width interchange type see §6. For arrays, rationals, and balls see the sections after that.
## 6. Software `Ieee32` / `Ieee64` (interchange)
These store IEEE-754 binary32 / binary64 as integer bits. Arithmetic is the same integer kernel as `ExactNum`, cut to 24- or 53-bit significands. Use them at an FFI or file boundary, not as a second calculator.
```rust
use zenith_float::{ExactNum, Ieee64};
let bits: u64 = 0x3FF0_0000_0000_0000; // 1.0
let x = Ieee64::from_bits(bits);
assert_eq!(x.to_bits(), bits);
let wide = x.to_exact(128);
let back = Ieee64::from_exact(&wide);
assert_eq!(back.to_bits(), bits);
let one = ExactNum::from_u8(1, 64);
assert_eq!(Ieee64::from_exact(&one).to_bits(), Ieee64::from_i32(1).to_bits());
```
`from_bits` / `to_bits` are the only way in or out of a host that already has hardware bits. Details: [LIBRARY.md](LIBRARY.md) §7b.
## 7. 2-D arrays
A 1-D vector is shape `(1, n)`. `from_shape` returns `None` if `rows * cols` is not `vals.len()`. Elementwise add/mul require matching shapes. `matmul` is a sequential triple loop (not BLAS).
```rust
use zenith_float::{ExactNum, ExactNumArray, Ieee64, Ieee64Array};
let p = 64;
let b = ExactNumArray::from_shape(p, 2, 2, &[n(5), n(6), n(7), n(8)]).unwrap();
let c = a.matmul(&b).unwrap();
assert_eq!(c.get2(0, 0).unwrap().cmp(&n(19)), Some(0));
assert!(a
.add(&ExactNumArray::from_shape(p, 2, 3, &[n(1), n(1), n(1), n(1), n(1), n(1)]).unwrap())
.is_none());
let u = Ieee64Array::from_shape(2, 2, &[Ieee64::from_i32(1); 4]).unwrap();
let v = u.matmul(&u).unwrap();
assert_eq!(v.get2(0, 0).unwrap().to_bits(), Ieee64::from_i32(2).to_bits());
```
CSV, HDF5 (feature `hdf5`), and the 16-byte binary record are in [LIBRARY.md](LIBRARY.md) §7b. Decompositions (`lu_decomp`, `svd_decomp`) return `None` if singular or a workspace reserve fails.
## 8. Specials at caller-chosen `(p, rm, cc)`
There is no process-wide precision. Every special takes the bit count, the rounding mode, and a `Consts` cache (when the algorithm needs π, e, or a similar constant).
```rust
use zenith_float::{Consts, ExactNum, RoundingMode};
let p = 256;
let rm = RoundingMode::ToEven;
let mut cc = Consts::new().expect("constants cache");
let x = ExactNum::from_u8(1, p);
let y = x.sin(p, rm, &mut cc); // sin(1)
```
Reuse `cc` in a loop. A new cache on every call recomputes π. Domain errors become `NaN`; `y.err()` is the `Error`. The same `(p, rm, cc)` arguments appear on `ExactNumArray` elementwise specials. Jacobi `sn`/`cn`/`dn` take parameter \(m=k^2\in[0,1]\); the inverse of `sn` is `elliptic_f`. See [HELP.md](HELP.md) recipe 32 and `# Precision` on each method.
## 9. `ExactRational` and `ExactInt`
`ExactRational` is reduced `num/den` (not a float). `0.1` parsed with `parse_exact` is `1/10`. `ExactInt` is a signed limb integer: factorials, `gcd`, `div_rem`. Convert to `ExactNum` only when you need a rounded float.
```rust
use zenith_float::{ExactInt, ExactRational};
let half = ExactRational::from_i64(1, 3).add(&ExactRational::from_i64(1, 6));
assert_eq!(half, ExactRational::from_i64(1, 2));
let g = ExactInt::from_i64(48).gcd(&ExactInt::from_i64(18));
assert_eq!(g, ExactInt::from_i64(6));
let (q, r) = ExactInt::from_i64(17).div_rem(&ExactInt::from_i64(5)).unwrap();
assert_eq!((q, r), (ExactInt::from_i64(3), ExactInt::from_i64(2)));
```
Inventory: [LIBRARY.md](LIBRARY.md) §7b.
## 10. Complex specials in `cexpr!`
`expr!` is real. `cexpr!` is complex: cancellation is tracked on each part. The imaginary unit is `I`. Do not name a variable `e` (that token is Euler’s number).
Principal cuts: `ln` and `sqrt` on the non-positive real axis (`ln(−1) = iπ`, `sqrt(−1) = +i`); \(K(m)\) and \({}_2F_1(\ldots;z)\) on \([1,+\infty)\).
```rust
use zenith_float::{cexpr, Consts, RoundingMode};
use zenith_float::ctx::Context;
let mut ctx = Context::new(
256,
RoundingMode::ToEven,
Consts::new().expect("constants cache"),
-10_000,
10_000,
);
let z = cexpr!(sqrt(-1), &mut ctx); // +i
let w = cexpr!(erf(I), &mut ctx);
```
Leaves include `erf` / `Γ` / `Ei` / Bessel / elliptic / `{}_2F_1`. Full list and cuts: [LIBRARY.md](LIBRARY.md) §17.
## 11. `Ball` enclosures
A `Ball` is `mid ± rad`. Transcendentals return a wider ball that still contains the image of the input disk (Lipschitz padding, not a tight interval library). `contains` is a software test at precision `p`. It does **not** prove a correctly rounded `ExactNum` by itself; `ziv_round` uses balls to certify a unique midpoint.
```rust
use zenith_float::{Ball, Consts, ExactNum, RoundingMode};
let p = 128;
let rm = RoundingMode::ToEven;
let mut cc = Consts::new().expect("constants cache");
let mid = ExactNum::from_u8(0, p);
let rad = ExactNum::from_u8(1, p);
let b = Ball::new(mid, rad);
let s = b.sin(p, rm, &mut cc);
assert!(s.contains(&ExactNum::from_u8(0, p), p));
```
`ComplexBall` is a disk in \(\mathbb{C}\). Contract: [LIBRARY.md](LIBRARY.md) (Ball / `ziv_round`).
## 12. Next documents
| [HELP.md](HELP.md) | Longer tutorial, recipes, FAQ |
| [LIBRARY.md](LIBRARY.md) | Every public type and method |
| [EXPR.md](EXPR.md) | Per-op working precision in `expr!` |
| [PRECISION.md](PRECISION.md) | Retry budget vs exponent |
| [README.md](README.md) | Error-bound theory (contributors) |