/** * TdcMath — the transcendental functions, computed by TDC rather than by the * host language. * * ── Why this file exists ───────────────────────────────────────────────────── * IEEE-754 pins down `+`, `-`, `*`, `/` and `sqrt`: each has exactly one legal * answer, so every language agrees about them. It says nothing about `sin`, * `cos`, `exp`, `log` or `pow` — every libm picks its own algorithm — and the * difference is real rather than theoretical. Measured on one machine, one day: * * tan(1) Node 3ff8eb245cbee3a6 Python 3ff8eb245cbee3a5 * cos(1000) Node 3fe1ff026793f1bb Python 3fe1ff026793f1bc * * Sixteen of seventy-seven sampled values disagree somewhere across the five * implementations. In `timeseries` that never shows: every number is rounded to * a decimal string before it becomes output, so the last bit dies on the way * out. An `if=` has no rounding step — a comparison turns that bit into a * different row, and a different file, on a tool whose whole promise is that * five implementations produce the same bytes. * * So TDC computes these itself, the way it already computes its own random * numbers rather than trusting each language's. * * ── The one rule ───────────────────────────────────────────────────────────── * **Nothing in this file may call a transcendental function of the host.** No * `Math.sin`, no `Math.exp`, no `Math.pow`. Only: * * + - * / IEEE-754 correctly rounded, one legal answer * Math.sqrt also correctly rounded by the standard — verified equal * across Node, Python, Java and Rust before being relied on * Math.abs, Math.floor, Math.trunc, comparisons — exact, no rounding at all * * One `Math.sin` slipped into one of the five ports would bring the divergence * straight back, and it would be invisible until a shared fixture compared the * bits. That fixture exists; keep it that way. * * ── What this is NOT ───────────────────────────────────────────────────────── * It is not an attempt to match the system libm, and not a claim of correct * rounding. The results here land within about 2 ulp of the true value, which * is the same neighbourhood a libm occupies. What matters is that all five * implementations land on the SAME double, and identical arithmetic gets that * for free. */ /** π and e as their nearest doubles. */ export declare const PI = 3.141592653589793; export declare const E = 2.718281828459045; /** * `sqrt` — delegated, and that is safe. * * IEEE-754 requires square root to be correctly rounded: there is one legal * answer and every implementation must give it. Measured across Node, Python, * Java and Rust before this line was written; all four agreed on every sample. */ export declare function sqrt(x: number): number; /** * `exp(x)` — range-reduced to `2^k · e^r` with |r| ≤ ln2/2, then Taylor. * * `k · LN2_HI` is exact: the constant carries 21 zero low bits, so any k this * reduction can produce multiplies without rounding, and the subtraction keeps * every digit a single ln2 constant would have thrown away. */ export declare function exp(x: number): number; /** * `log(x)` — natural logarithm. * * `x` is split into `m · 2^e` with m near 1 by halving and doubling, both exact. * Then `log(m) = 2·atanh(s)` with `s = (m−1)/(m+1)`, whose series converges fast * because |s| ≤ 0.1716 over the reduced range. */ export declare function log(x: number): number; /** * `log10(x)`. * * A power of ten is the argument someone passes to `log10`, and `log(x)/ln10` * gets it wrong: it returned 2.9999999999999996 for 1000. There is no exponent * to separate here the way `log2` separates a power of two — a double is binary * — so the whole answer is checked instead: if raising ten back to it returns * the argument unchanged, it was exact and is given as a whole number. * * The check needs no tolerance. `pow` with a whole exponent goes through * repeated squaring, so the equality is the test. */ export declare function log10(x: number): number; export declare function sin(x: number): number; export declare function cos(x: number): number; /** * `tan(x)` — one reduction shared by both halves, so numerator and denominator * can never come from different quadrants. * * The four quadrants collapse to two cases: an even one leaves sin over cos, an * odd one swaps them and flips the sign. */ export declare function tan(x: number): number; export declare function pow(x: number, y: number): number; /** * `atan(x)` — the arctangent, in radians, over the whole real line. * * Above 1 the argument is flipped with `atan(x) = π/2 − atan(1/x)`, which keeps * the series on the interval it was built for and makes the infinities fall out * for free. */ export declare function atan(x: number): number; /** * `atan2(y, x)` — the angle of the point (x, y), in radians, over (−π, π]. * * The quadrant cannot be recovered from `y/x` alone: the ratio is the same in * opposite quadrants, which is the whole reason this function exists separately * from `atan`. */ export declare function atan2(y: number, x: number): number; /** * `asin(x)` — the arcsine, in radians, over [−1, 1]. * * Past a half the direct route would compute `1 − a²` with a and 1 nearly * equal, and lose most of its digits before `sqrt` ever saw them. The * half-angle identity `asin(a) = π/2 − 2·asin(√((1−a)/2))` moves the * subtraction to `1 − a`, which is exact in that range, and lands back on the * branch above. */ export declare function asin(x: number): number; /** * `acos(x)` — the arccosine, in radians, over [−1, 1]. * * Not `π/2 − asin(x)` everywhere: near x = 1 the answer approaches zero, and * that subtraction would compute it as the difference of two numbers that are * nearly π/2, throwing away every digit that matters. Each end gets the form * that keeps them. */ export declare function acos(x: number): number; /** * `sinh(x)` — the hyperbolic sine. * * Below a half the exponential route would compute `eˣ − e⁻ˣ` with the two * nearly equal and cancel away the answer, so the series takes over there. * Above it there is no cancellation left to fear. */ export declare function sinh(x: number): number; /** * `cosh(x)` — the hyperbolic cosine. * * A sum rather than a difference, so nothing cancels; the series below a half * is for accuracy, not for survival. */ export declare function cosh(x: number): number; /** * `tanh(x)` — the hyperbolic tangent. * * Past 20 the true value is within 10⁻¹⁷ of 1, closer than the next double, so * the answer is 1 and computing e⁴⁰ to discover that would be waste. */ export declare function tanh(x: number): number; /** * `cbrt(x)` — the cube root, defined for negatives too. * * `pow(x, 1/3)` is not the same function: 1/3 is not a double, and a negative * base with a fractional exponent has no real answer at all. So this is its own * function, reduced by powers of eight — exact, being powers of two — and then * refined by Newton's method, which triples its correct digits each pass. */ export declare function cbrt(x: number): number; /** * `expm1(x)` — eˣ − 1, computed so that small x keeps its digits. * * `exp(0.0000001) - 1` in plain arithmetic is a subtraction of two numbers that * agree to seven places, and most of the answer dies in it. The series has no * subtraction to lose anything to. */ export declare function expm1(x: number): number; /** * `log1p(x)` — log(1 + x), computed so that small x keeps its digits. * * The loss here happens before the logarithm is reached: `1 + 1e-20` IS 1 as a * double, so `log(1 + x)` returns zero for every x under 10⁻¹⁶. Reducing * instead to `2·atanh(x/(2+x))` never forms `1 + x` at all. */ export declare function log1p(x: number): number; /** * `log2(x)`. * * Not `log(x) / ln2`: that would make `log2(8)` come out 2.9999999999999996, * and a power of two is precisely the argument someone passes to `log2`. The * exponent is separated first, so a power of two returns a whole number exactly * and only the mantissa goes through the logarithm. */ export declare function log2(x: number): number; /** * `hypot(x, y)` — the length of the vector, without an intermediate that * overflows. * * `sqrt(x*x + y*y)` is the definition and the wrong implementation: for * x = 10²⁰⁰ the square overflows to infinity and the answer comes back * infinite, though it is perfectly representable. Factoring the larger side out * first keeps every intermediate near 1. */ export declare function hypot(x: number, y: number): number; /** `sign(x)` — −1, 0 or 1. Exact: there is nothing here to round. */ export declare function sign(x: number): number; /** * `asinh(x)` — the inverse hyperbolic sine, over the whole real line. * * `log(x + sqrt(x² + 1))` is the textbook form and cancels for small x: the two * terms approach 1 and −1 of each other. Rewriting the argument as * `x + x²/(1 + sqrt(1 + x²))` leaves `log1p` a number near x rather than a * number near 1, and nothing cancels. */ export declare function asinh(x: number): number; /** * `acosh(x)` — the inverse hyperbolic cosine, defined for x ≥ 1. * * Written around `t = x − 1`, which is exact for the x near 1 where the answer * approaches zero and the textbook form loses it. */ export declare function acosh(x: number): number; /** * `atanh(x)` — the inverse hyperbolic tangent, over (−1, 1). * * `½·log((1+x)/(1−x))` forms a ratio near 1 for small x and loses it. The same * ratio written as `1 + 2x/(1−x)` hands `log1p` the small part directly. */ export declare function atanh(x: number): number; /** * `erf(x)` — the error function. * * Below 1 the series is used directly; above it, `1 − erfc(x)`, because there * erfc is the small quantity and the subtraction costs nothing. */ export declare function erf(x: number): number; /** * `erfc(x)` — the complement, 1 − erf(x), and not computed that way past 1. * * At x = 5 the true value is 1.5·10⁻¹², which `1 − erf(x)` cannot produce at * all: erf(5) rounds to 1, and the subtraction gives zero. Twelve significant * digits, gone. That is the reason this function exists separately. */ export declare function erfc(x: number): number; /** * `lgamma(x)` — the natural logarithm of |Γ(x)|. * * ── What its accuracy actually is ──────────────────────────────────────────── * Away from x = 1 and x = 2 it is within 3 ulp. AT those two points lgamma is * ZERO, and a relative bound there is not a statement about this code — no * method that sums terms of size 1 can be relatively accurate about their * cancelling to nothing. What holds everywhere is the ABSOLUTE error, measured * under 10⁻¹³, and both zeros come out exactly zero. */ export declare function lgamma(x: number): number; /** * `gamma(x)` — Γ(x), the factorial extended to the reals. * * ── Why a whole number takes a different route ─────────────────────────────── * Γ of a whole number is a factorial, and multiplying it out is both exact for * the first twenty-three and within 7 ulp for all 171 that fit in a double. The * general route cannot match that: it ends in an exponential, and `exp` turns * the absolute error of its argument into a relative error of its answer — so * the drift grows with log Γ(x) itself, reaching about 2000 ulp near x = 146. * The same amplification `pow` has, for the same reason. */ export declare function gamma(x: number): number; /** * `degrees(x)` and `radians(x)` — one multiplication each, and exact in the * sense that matters: there is a single rounding, so all five agree trivially. * * They exist because every circular function here takes radians and says so. * A config holding an angle in degrees needs somewhere to put the conversion, * and a named function is better than the constant 57.29577951308232 appearing * in it by hand. */ export declare function degrees(x: number): number; export declare function radians(x: number): number; /** * `beta(a, b)` — the Euler beta function, Γ(a)Γ(b)/Γ(a+b). * * Taken directly from the gammas while they fit, because that is the accurate * route. Past a+b = 171 the numerator overflows although the answer is small — * beta SHRINKS as its arguments grow — so up there it goes through logarithms * instead, at the cost of the usual exponential amplification. */ export declare function beta(a: number, b: number): number; /** * `digamma(x)` — ψ(x), the derivative of log Γ(x). * * Small arguments are lifted by the recurrence until the asymptotic series is * usable; negatives come back through the reflection ψ(1−x) − ψ(x) = π·cot(πx). */ export declare function digamma(x: number): number; /** * `zeta(s)` — the Riemann zeta function, for real s. * * Above 1 the sum converges and Euler–Maclaurin makes twelve terms behave like * thousands. Below it the functional equation reflects the argument across * s = ½, which is why this needs Γ and sin and could not have been written * before them. ζ(1) is the pole; ζ(0) is −½, which the reflection cannot give * because it meets 0 · ∞ there. */ export declare function zeta(s: number): number; //# sourceMappingURL=tdc-math.d.ts.map