FixedPrecision is a library for handling fixed‑precision decimal numbers in JavaScript/TypeScript. By storing scaled bigint values internally, this library enables precise arithmetic, detailed control over decimal places, and various rounding modes — ideal for avoiding floating‑point imprecision.
- Configurable precision — 0 to 20 decimal places.
- 9 rounding modes — ROUND_UP, ROUND_DOWN, ROUND_CEIL, ROUND_FLOOR, ROUND_HALF_UP, ROUND_HALF_DOWN, ROUND_HALF_EVEN, ROUND_HALF_CEIL, ROUND_HALF_FLOOR.
- Full arithmetic — addition, subtraction, multiplication, division, modulo, exponentiation, square root, cube root, negation, integer division.
- Method chaining — arithmetic and comparison directly with
number,string, orbigintwithout explicit instantiation. - Flexible conversions —
toString,toNumber,toFixed,toExponential,toPrecision,toFormat,toJSON,toBinary,toOctal,toHex. - Rounding & scaling —
round,prec,ceil,floor,trunc,scale,shiftedBy,clamp,toNearest. - Comparisons —
cmp,eq,gt,gte,lt,lte(plus raw variants). - Predicates —
isZero,isPositive,isNegative,isInteger; logical operations (sign,not,and,or,xor). - Logarithms —
ln,log,log2,log10,exp. - Trigonometry —
sin,cos,tan,sec,csc,cotand their inverse, hyperbolic, and inverse‑hyperbolic counterparts (26 functions total, includingatan2). - Statistics —
min,max,sum,hypot,random. - Math constants —
PI,e,phi,sqrt2. - Combinatorics —
factorial,permutations,combinations. - Vector / matrix —
dot,cross. - Fractions —
num,den,fraction. - Bitwise operations —
bitAnd,bitOr,bitXor,bitNot,leftShift,rightArithShift. - TypeScript — full type definitions included (
FixedPrecisionValue,FixedPrecisionConfig,RoundingMode,Comparison).
npm install fixed-precisionimport FixedPrecision, { fixedconfig } from "fixed-precision";
fixedconfig.configure({ places: 8, roundingMode: 4 });
const a = new FixedPrecision("1.2345");
const b = new FixedPrecision(2.3456);
const sum = a.add(b);
console.log(sum.toString()); // "3.5801"
const product = a.mul(b);
console.log(product.toString()); // "2.8955832"
// Method chaining with raw values
const result = new FixedPrecision(10.5).add(5).div(3).toNumber();
console.log(result); // 5.16666667
// Mixed input types
new FixedPrecision(100).add("50").sub(25n).mul(2).div("5").toString(); // "50"Set default decimal places and rounding mode for all new FixedPrecision() instances that don't receive an explicit context.
import { fixedconfig } from "fixed-precision";
// or: FixedPrecision.configure({ ... })
fixedconfig.configure({
places: 8, // 0–20
roundingMode: 4, // ROUND_HALF_UP (default)
});| Code | Name | Behavior |
|---|---|---|
| 0 | ROUND_UP | Rounds away from zero |
| 1 | ROUND_DOWN | Rounds toward zero (truncation) |
| 2 | ROUND_CEIL | Rounds toward +Infinity |
| 3 | ROUND_FLOOR | Rounds toward -Infinity |
| 4 | ROUND_HALF_UP | Rounds half away from zero |
| 5 | ROUND_HALF_DOWN | Rounds half toward zero |
| 6 | ROUND_HALF_EVEN | Rounds half to the nearest even number |
| 7 | ROUND_HALF_CEIL | Rounds half toward +Infinity |
| 8 | ROUND_HALF_FLOOR | Rounds half toward -Infinity |
For applications that need multiple precisions at the same time, use immutable factories instead of mutating the global configuration.
import FixedPrecision, { fixedconfig } from "fixed-precision";
// Independent, immutable contexts
const FP8 = FixedPrecision.create({ places: 8, roundingMode: 4 });
const FP2 = FixedPrecision.create({ places: 2 });
const a = FP8("1.23456789"); // "1.23456789"
const b = FP2("1.23"); // "1.23"
// Method chaining works with factories too
const c = FP8(10.5).add(5).div(3);
// Factories are unaffected by global config changes
fixedconfig.configure({ places: 4 });
FP8("1").toString(false); // "1.00000000" (unchanged)
FP2("1").toString(false); // "1.00" (unchanged)
new FixedPrecision("1").toString(false); // "1.0000" (global default)- Performance — No per‑instance option parsing; the factory captures its scale and rounding mode once.
- Clarity — The precision is explicit in the factory variable name.
- Safety — No shared mutable state; cross‑context arithmetic throws an error.
- Flexibility — Create as many contexts as needed and pass them around.
FixedPrecision.create(config: FixedPrecisionConfig): (val: FixedPrecisionValue) => FixedPrecisionconfig.places— integer 0–20 (required).config.roundingMode— integer 0–8 (optional, defaults to 4 / ROUND_HALF_UP).- The returned function creates instances locked to that configuration.
factory.formatexposes the frozen{ places, roundingMode }object.
For more detailed guides and examples, see the docs/ directory:
| Category | Topics |
|---|---|
| Getting Started | Quick Start, Installation, Basic Concepts |
| Core Features | Arithmetic, Raw Operations, Rounding & Scaling, Conversion, Minimal Build |
| Configuration | Global Configuration, Precision Factories |
| Advanced | Performance, Error Handling, BigInt Warning |
| API | Full API Reference, Type Definitions |
| Integration | Migration Guide, Integration, Testing |
new FixedPrecision(value: FixedPrecisionValue, ctx?: FPContext)Accepts string | number | bigint | FixedPrecision.
string/number→ parsed as a decimal value and scaled to the current context.bigint→ treated as already scaled (pre‑scaled). See BigInt Warning.FixedPrecision→ reused directly (validation ensures same precision context).
All arithmetic methods accept FixedPrecisionValue (string | number | bigint | FixedPrecision).
Scaled operations (maintain decimal precision, validate configuration):
| Instance | Static | Description |
|---|---|---|
add(v) |
FixedPrecision.add(a, b) |
Addition |
sub(v) |
FixedPrecision.sub(a, b) |
Subtraction |
mul(v) |
FixedPrecision.mul(a, b) |
Multiplication |
div(v) |
FixedPrecision.div(a, b) |
Division |
mod(v) |
FixedPrecision.mod(a, b) |
Modulo |
pow(n) |
FixedPrecision.pow(v, n) |
Exponentiation (integer) |
sqrt() |
FixedPrecision.sqrt(v) |
Square root |
square() |
FixedPrecision.square(v) |
Square (value²) |
cube() |
FixedPrecision.cube(v) |
Cube (value³) |
cbrt() |
FixedPrecision.cbrt(v) |
Cube root |
neg() |
— | Negation (-value) |
abs() |
FixedPrecision.abs(v) |
Absolute value |
idiv(v) |
— | Integer division |
divmod(v) |
— | Division → { quotient, remainder } |
idivmod(v) |
— | Integer division → { quotient, remainder } |
Raw operations (operate directly on scaled values, no configuration validation):
| Instance | Description (raw) |
|---|---|
plus(v) |
Addition without scaling |
minus(v) |
Subtraction without scaling |
times(v) |
Multiplication without scaling |
ratio(v) |
Division without scaling |
rem(v) |
Remainder without scaling |
Regular comparisons (validate configuration):
| Instance | Static | Returns |
|---|---|---|
cmp(v) |
— | -1 | 0 | 1 |
eq(v) |
— | boolean |
gt(v) |
— | boolean |
gte(v) |
— | boolean |
lt(v) |
— | boolean |
lte(v) |
— | boolean |
Raw comparisons (compare scaled values directly, no validation — main build only):
cmpRaw(v), eqRaw(v), gtRaw(v), gteRaw(v), ltRaw(v), lteRaw(v).
| Method | Description |
|---|---|
round(dp?, rm?) |
Round to dp decimal places |
prec(sd, rm?) |
Round to sd significant digits |
ceil() |
Round up to integer |
floor() |
Round down to integer |
trunc() |
Truncate toward zero |
scale(newScale, rm?) |
Rescale to newScale decimal places |
shiftedBy(n) |
Shift decimal point by n places |
clamp(min, max) |
Clamp value between min and max (main only) |
toNearest(increment, rm?) |
Round to nearest multiple of increment (main only) |
| Method | Description |
|---|---|
toString() |
Decimal string representation |
toNumber(places?) |
Convert to number |
toFixed(places?, rm?) |
Fixed‑point notation string |
toExponential(dp?, rm?) |
Scientific notation string |
toPrecision(sd, rm?) |
Format to significant digits |
toFormat(dp?, rm?) |
String with thousands separators (minimal build) |
toJSON() |
JSON serialization (same as toString()) |
valueOf() |
Returns toString() |
toBinary(sd?, rm?) |
Binary string (main only) |
toOctal(sd?, rm?) |
Octal string (main only) |
toHex(sd?, rm?) / toHexadecimal() |
Hex string (main only) |
| Method | Description |
|---|---|
isZero() |
true if value is exactly zero |
isPositive() |
true if value > 0 |
isNegative() |
true if value < 0 |
isInteger() |
true if value has no fractional part |
sign() |
Returns -1, 0, 1, or NaN (main only) |
not() |
Logical NOT — true if zero (main only) |
and(v), or(v), xor(v) |
Logical AND / OR / XOR (main only) |
places() / decimalPlaces() |
Returns context decimal places (main only) |
precision(z?) / sd(z?) |
Returns significant digits (main only) |
raw() |
Returns the internal bigint (main only) |
typeof() |
Returns "FixedPrecision" (main only) |
| Instance | Static | Description |
|---|---|---|
ln() |
FixedPrecision.ln(v) |
Natural logarithm |
log(b?) |
FixedPrecision.log(v, b?) |
Logarithm (base optional) |
log2() |
FixedPrecision.log2(v) |
Base‑2 logarithm |
log10() |
FixedPrecision.log10(v) |
Base‑10 logarithm |
exp() |
FixedPrecision.exp(v) |
e raised to the value |
Standard: sin(), cos(), tan()
Reciprocal: sec(), csc(), cot()
Inverse: asin(), acos(), atan(), atan2(x)
Inverse reciprocal: acot(), asec(), acsc()
Hyperbolic: sinh(), cosh(), tanh()
Reciprocal hyperbolic: sech(), csch(), coth()
Inverse hyperbolic: asinh(), acosh(), atanh()
Inverse reciprocal hyperbolic: asech(), acsch(), acoth()
All are available as both instance (value.sin()) and static (FixedPrecision.sin(value)) methods.
| Static method | Description |
|---|---|
FixedPrecision.min(...vals) |
Minimum value (array or rest) |
FixedPrecision.max(...vals) |
Maximum value (array or rest) |
FixedPrecision.sum(...vals) |
Sum of values (array or rest) |
FixedPrecision.hypot(...vals) |
Hypotenuse — sqrt(sum of squares) |
FixedPrecision.random(decimalPlaces?) |
Random value 0–1 at given places |
| Static method | Value |
|---|---|
FixedPrecision.PI() |
π (pi) |
FixedPrecision.e() |
Euler's number |
FixedPrecision.phi() |
Golden ratio (1+√5)/2 |
FixedPrecision.sqrt2() |
√2 |
FixedPrecision.factorial(n), FixedPrecision.permutations(n, k), FixedPrecision.combinations(n, k).
| Instance | Description |
|---|---|
.num() |
Numerator of the reduced fraction |
.den() |
Denominator of the reduced fraction |
.fraction(maxDen?) |
Best rational approximation [num, den] |
bitAnd(v), bitOr(v), bitXor(v), bitNot(), leftShift(n), rightArithShift(n) — operate directly on the raw scaled bigint.
FixedPrecision.dot(a, b) → scalar dot product.
FixedPrecision.cross(a, b) → returns an array (cross product).
All arithmetic and comparison methods accept FixedPrecisionValue (string | number | bigint | FixedPrecision), enabling concise chaining without explicit instantiation:
new FixedPrecision(100).add("50").sub(25n).mul(2).div("5"); // "50"
new FixedPrecision(10.5).add(5).div(3).gt(5); // true
new FixedPrecision("1.23").plus("2.00").times("3.00"); // raw chainType behavior:
string/number→ decimal values, automatically scaled.bigint→ treated as already scaled (pre‑scaled). See below.FixedPrecision→ requires matching precision context (for scaled operations).
critical:
bigintvalues are treated as pre‑scaled, not as decimal values.
new FixedPrecision("1.23"); // value = 123000000 (assuming 8 decimal places)
new FixedPrecision(1.23); // value = 123000000
new FixedPrecision(123000000n); // value = 123000000 (pre‑scaled)
new FixedPrecision(123n); // value = 123 (0.00000123 — NOT 123.00!)When chaining:
const a = new FixedPrecision("1.23");
a.add(2); // adds 2.00000000 (number → scaled)
a.add(2n); // adds 0.00000002 (bigint → pre‑scaled!)
a.add("2.00"); // adds 2.00000000 (string → scaled)
a.add(200000000n); // adds 2.00000000 (bigint → pre‑scaled correctly)Use bigint only when you have pre‑calculated scaled values. For literal decimal values, use number or string.
Import a smaller entry point when you only need core decimal operations:
import FixedPrecision, { fixedconfig } from "fixed-precision/minimal";The minimal build includes: creation, arithmetic, comparison, rounding/scaling, sqrt, random/min/max/sum, predicates, and common formatting/conversion. It omits the larger extended APIs: trigonometry, logarithms, matrix/vector, bitwise, combinatorics, fractions, constants, logical operations, raw comparisons, and advanced inspection methods.
- Fork the repository.
- Create a new branch:
git checkout -b feature-name. - Commit your changes:
git commit -m 'Add some feature'. - Push to the branch:
git push origin feature-name. - Open a pull request.
Distributed under the MIT License. See the LICENSE file for more details.