Companion for _Rational: smart factories, constants, and the shared reduction step.
Attributes
Members list
Value members
Concrete methods
Builds a rational from an integer.
Builds a rational from an integer.
Value parameters
- n
-
the integer value
Attributes
- Returns
-
n/1
Builds a rational from an Int.
Builds a rational from an Int.
Value parameters
- n
-
the integer value
Attributes
- Returns
-
n/1
Whether e carries an exact value anywhere in it.
Whether e carries an exact value anywhere in it.
The question every algorithm that synthesises a numeric constant has to answer. Float contagion is about the tier, not the value: _Number(-1) is an exact integer but an inexact-tier one, so multiplying an exact operand by it correctly demotes the result. That is right when the -1 came from user input and wrong when the algorithm invented it — Sum(lhs, Product(_Number(-1), rhs)) would silently pull an entire exactly-stated equation into Double before the solver ever saw it.
Value parameters
- e
-
the expression to inspect
Attributes
- Returns
-
truewhen any node is a_Rational
Brings a Double result of a non-closed operation back into the exact tier.
Brings a Double result of a non-closed operation back into the exact tier.
Used wherever the rationals are left and re-entered — fractional powers and the transcendental functions. The requested precision is capped at DoubleReliableDigits so the result claims no more accuracy than its source had; without that cap the "approximation" would faithfully reproduce the Double's dyadic expansion and call it thirty digits.
Value parameters
- d
-
the computed value
- digits
-
the working precision requested
Attributes
- Returns
-
the rational approximation, or
Nonewhendis NaN or infinite
Converts a decimal literal to the exact rational it denotes.
Converts a decimal literal to the exact rational it denotes.
The counterpart of fromDouble, and the one the parser uses. The distinction is the whole reason exact mode has to live at parse time: fromDecimalString("0.1") is 1/10, while fromDouble(0.1) is the dyadic the hardware rounded it to. Only the first makes 0.1 + 0.2 == 0.3 come out exactly.
Exponents are handled exactly too, so 1e-320 is 1/10^320 rather than the Double denormal it would otherwise become.
Value parameters
- s
-
the literal as written
Attributes
- Returns
-
the exact rational, or
Nonewhensis not a decimal literal
Converts a Double to the exact rational it denotes.
Converts a Double to the exact rational it denotes.
Every finite Double is a dyadic rational, so this conversion is lossless — it recovers the value the hardware actually holds, not the decimal literal that was written. fromDouble(0.1) is therefore 3602879701896397/36028797018963968, which is the honest answer and the reason a number that was already parsed as a Double cannot be repaired after the fact — hence the parser's own exact mode, and hence fromDecimalString beside this.
Value parameters
- d
-
the value to convert
Attributes
- Returns
-
the exact rational, or
Nonewhendis NaN or infinite
An integer constant in the same tier as like.
An integer constant in the same tier as like.
Use wherever an algorithm needs a constant of its own — a -1 to negate with, the 2 of a quadratic denominator — so that it joins the expression it is about to combine with rather than dragging it down a tier. The parser's literalInt is the parse-time counterpart of this.
Value parameters
- like
-
an expression from the surrounding computation
- n
-
the integer value
Attributes
- Returns
-
a
_Rationalwhenlikeis exact, a_Numberotherwise
Builds a rational from a numerator and denominator.
Builds a rational from a numerator and denominator.
Returns None for a zero denominator rather than throwing, following the house rule that an undefined result leaves the caller symbolic instead of propagating an infinity (the same contract as SpecialFunctions' kernels).
Value parameters
- d
-
the denominator
- n
-
the numerator
- policy
-
the reduction policy applied to the result
Attributes
- Returns
-
the rational
n/d, orNonewhendis zero
The reduction bound appropriate to a given working precision.
The reduction bound appropriate to a given working precision.
8 · digits is a little over twice the digits · log2 10 ≈ 3.32 · digits bits one re-approximated operand occupies, which is what a product of two of them needs before reduction is worth paying for. Floored at DefaultThresholdBits so a low working precision does not collapse the policy back onto GcdPolicy.Eager.
Value parameters
- digits
-
the working precision in decimal digits
Attributes
- Returns
-
a GcdPolicy.Threshold sized for that precision
Concrete fields
The reduction policy chosen by the gcd-policy benchmark, and what a caller with no opinion should get.
The reduction policy chosen by the gcd-policy benchmark, and what a caller with no opinion should get.
GcdPolicy.Lazy was rejected on the exact arm — where nothing re-approximates, which is the normal mode for the closed operations (Sum, Product, Ratio, integer Power, and all of matrix) rather than a synthetic control. There it reached 2.5 million bits and 2.65 GB on an 8×8 Hilbert solve against Eager's 30 bits, an 83 000× operand-size ratio. Threshold keeps Lazy's cheapness wherever re-approximation is already bounding growth, and behaves as a guard where it is not.
Attributes
Default bit-length bound for GcdPolicy.Threshold, chosen by the gcd-policy benchmark.
Default bit-length bound for GcdPolicy.Threshold, chosen by the gcd-policy benchmark.
It has to clear the operand size a working precision implies, or the policy fires on every operation and is GcdPolicy.Eager under another name — the benchmark shows this directly, with a 64-bit bound at 30 working digits reproducing Eager's operand sizes exactly (105 / 30 bits) and a 256-bit bound reproducing Lazy's (186 / 75). A 30-digit value needs 30 · log2 10 ≈ 100 bits and a product of two reaches ~200, so 256 clears it with headroom. See thresholdFor for the scaling rule at other precisions.
Attributes
Significant decimal digits a Double actually carries.
Significant decimal digits a Double actually carries.
The ceiling on fromApproximation, and an honest one: an operation that leaves the rationals — every transcendental, and any fractional power — is currently evaluated in Double and re-approximated, so asking for thirty digits of sin(1/3) would manufacture fifteen digits that are not there. Lifting this needs an arbitrary precision engine for the functions themselves (spire's Real); the working precision meanwhile bounds the arithmetic around them, which is where the cancellation lives.
Attributes
Longest numerator or denominator, in decimal digits, still shown as a fraction.
Longest numerator or denominator, in decimal digits, still shown as a fraction.
Beyond this a _Rational displays as a decimal instead. The bound exists because the two things a rational can be are very different to read: 1/3 is clearer as a fraction than as 0.33333, while a 30-digit rational approximation of pi is a 31-over-31-digit wall that tells the reader nothing. Display only — Session serializes the exact fraction regardless, so :save never loses the value.
Attributes
Size ceiling, in bits, for the result of an exact integer power.
Size ceiling, in bits, for the result of an exact integer power.
2^20 bits is roughly a 315 000-digit number: far past anything a session needs, and still computed in milliseconds. The cap exists for the same reason scalar.MaxExactFactorial does — "computable in principle" is not "should be attempted". BigInt.pow has no bound of its own, so in exact mode a typo such as (123/7)^2000000000 asks for a ~1.4e10-bit operand and hangs or OOMs the session rather than answering. The estimate is maxBitLength × |k|, which needs no allocation to compute; past it _Rational.pow returns None and the expression stays symbolic, the give-up rule used throughout the library.