Rational

Rational represents an exact ratio of two arbitrary-precision integers. It always reduces to lowest terms and keeps the denominator positive.

Construction and constants

import { Rational } from "@ratmath/core";

new Rational(6, -8).toString(); // "-3/4"
new Rational("2..1/3").toString(); // "7/3"
Rational.from(5n).toString();   // "5"

Rational.zero.toString();       // "0"
Rational.one.toString();        // "1"

The constructor accepts a numerator plus an optional denominator. A string constructor accepts integers, fractions, mixed fractions, and finite decimals; use the parsing helpers for repeating decimals, continued fractions, or intervals. A zero denominator throws.

The read-only numerator and denominator properties are normalized bigints.

Arithmetic and comparison

Method Behavior
add(other) Exact sum; an interval operand produces RationalInterval
subtract(other) Exact difference; an interval operand produces RationalInterval
multiply(other) Exact product; an interval operand produces RationalInterval
divide(other) Exact quotient; division by zero throws
negate() Additive inverse
reciprocal() Swaps numerator and denominator; zero throws
pow(exponent) Exact integer power, including negative powers
abs() Absolute value
equals(other) Mathematical equality
compareTo(other) -1, 0, or 1
lessThan, lessThanOrEqual Ordered comparisons
greaterThan, greaterThanOrEqual Ordered comparisons
E(exponent) Multiply by exact 10^exponent
bitLength() Maximum of numerator and denominator bit lengths
const x = new Rational(2, 3);

x.add(new Rational(5, 6)).toString(); // "3/2"
x.pow(-2).toString();                 // "9/4"
x.E(3).toString();                    // "2000/3"
x.lessThan(new Rational(3, 4));       // true

0^0, zero to a negative power, and a non-integer exponent throw.

Fraction, mixed, and numeric output

Method Result
toString(base?) Reduced fraction, or an integer when denominator is one
toMixedString() RiX mixed form such as -2..1/4
toNumber() JavaScript number; potentially inexact
toDecimal() Terminating decimal or a display truncation of at most 20 fractional digits
const x = new Rational(-9, 4);

x.toString();      // "-9/4"
x.toMixedString(); // "-2..1/4"
x.toNumber();      // -2.25
x.toDecimal();     // "-2.25"

Repeating decimal output

Method Result
toRepeatingDecimal() Exact base-10 string using # repeat notation
toRepeatingDecimalWithPeriod(useRepeatNotation?) { decimal, period }
computeDecimalMetadata(maxPeriodDigits?) Segments and period metadata used by formatters
extractPeriodSegment(initial, periodLength, digits) Repeats/truncates a known period segment to the requested length
toScientificNotation(useRepeatNotation?, precision?, showPeriodInfo?) Decimal scientific display
const x = new Rational(1, 6);

x.toRepeatingDecimal(); // "0.1#6"
x.toRepeatingDecimalWithPeriod();
// { decimal: "0.1#6", period: 1 }

x.computeDecimalMetadata();
// includes initialSegment: "1", periodDigits: "6",
// periodLength: 1, isTerminating: false

new Rational(12345).toScientificNotation(); // "1.2345E4"

Rational.DEFAULT_PERIOD_DIGITS, MAX_PERIOD_DIGITS, and MAX_PERIOD_CHECK are the public limits used by these formatting operations. For exact interchange, prefer toString() or toRepeatingDecimal().

Arbitrary-base output

Method Result
toBase(system) Integer or numerator/denominator digits; no radix expansion
toRepeatingBase(system) Exact radix expansion with # before the repeating block
toRepeatingBaseWithPeriod(system, options?) { baseStr, period, limitHit }
periodModulo(system, limit?) Multiplicative-order period length, or 0 when terminating; throws if the iteration limit is exceeded
import { BaseSystem } from "@ratmath/core";

const third = new Rational(1, 3);

third.toBase(BaseSystem.BINARY);          // "1/11"
third.toRepeatingBase(BaseSystem.BINARY); // "0.#01"
third.toRepeatingBaseWithPeriod(BaseSystem.BINARY);
// { baseStr: "0.#01", period: 2, limitHit: false }

toRepeatingBaseWithPeriod accepts useRepeatNotation (default true) and limit. If the digit-generation limit is reached, limitHit is true and the returned expansion is partial.

Continued fractions

Method Result
toContinuedFraction(maxTerms?) Array of bigint coefficients
toContinuedFractionString() RiX a0.~a1~... representation
convergents(maxCount?) Successive rational convergents
getConvergent(index) Zero-based convergent; throws if out of range
bestApproximation(maxDenominator) Last convergent whose denominator does not exceed the bound
approximationError(target) Absolute difference from the target
Rational.fromContinuedFraction(coefficients) Rational from a finite coefficient array
Rational.fromContinuedFractionString(text) Rational from .~ notation
Rational.convergentsFromCF(input, maxCount?) Convergents from an array or string
const value = new Rational(333, 106);

value.toContinuedFraction();       // [3n, 7n, 15n]
value.toContinuedFractionString(); // "3.~7~15"
value.convergents().map((v) => v.toString());
// ["3", "22/7", "333/106"]

Rational.fromContinuedFraction([3n, 7n, 15n]).toString();
// "333/106"

new Rational(355, 113).bestApproximation(100n).toString();
// "22/7"

Rational.DEFAULT_CF_LIMIT is the default maximum coefficient/convergent count. Continued fractions here are finite because every Rational is exact and rational.