Skip to content

naoeosavio/fixed-precision

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

322 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FixedPrecision

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.

Features

  • 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, or bigint without explicit instantiation.
  • Flexible conversionstoString, toNumber, toFixed, toExponential, toPrecision, toFormat, toJSON, toBinary, toOctal, toHex.
  • Rounding & scalinground, prec, ceil, floor, trunc, scale, shiftedBy, clamp, toNearest.
  • Comparisonscmp, eq, gt, gte, lt, lte (plus raw variants).
  • PredicatesisZero, isPositive, isNegative, isInteger; logical operations (sign, not, and, or, xor).
  • Logarithmsln, log, log2, log10, exp.
  • Trigonometrysin, cos, tan, sec, csc, cot and their inverse, hyperbolic, and inverse‑hyperbolic counterparts (26 functions total, including atan2).
  • Statisticsmin, max, sum, hypot, random.
  • Math constantsPI, e, phi, sqrt2.
  • Combinatoricsfactorial, permutations, combinations.
  • Vector / matrixdot, cross.
  • Fractionsnum, den, fraction.
  • Bitwise operationsbitAnd, bitOr, bitXor, bitNot, leftShift, rightArithShift.
  • TypeScript — full type definitions included (FixedPrecisionValue, FixedPrecisionConfig, RoundingMode, Comparison).

Installation

npm install fixed-precision

Quick Start

import 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"

Global Configuration

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)
});

Rounding Modes

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

Precision Factories (Recommended)

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)

Why factories?

  • 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.

Factory API

FixedPrecision.create(config: FixedPrecisionConfig): (val: FixedPrecisionValue) => FixedPrecision
  • config.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.format exposes the frozen { places, roundingMode } object.

Documentation

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

API Reference

Constructor

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).

Arithmetic

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

Comparison

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).

Rounding & Scaling

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)

Formatting & Conversion

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)

Predicates & Inspection

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)

Logarithms (main build 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

Trigonometry (main build only)

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.

Statistics

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

Constants (main build only)

Static method Value
FixedPrecision.PI() π (pi)
FixedPrecision.e() Euler's number
FixedPrecision.phi() Golden ratio (1+√5)/2
FixedPrecision.sqrt2() √2

Combinatorics (main build only)

FixedPrecision.factorial(n), FixedPrecision.permutations(n, k), FixedPrecision.combinations(n, k).

Fraction (main build only)

Instance Description
.num() Numerator of the reduced fraction
.den() Denominator of the reduced fraction
.fraction(maxDen?) Best rational approximation [num, den]

Bitwise Operations (main build only)

bitAnd(v), bitOr(v), bitXor(v), bitNot(), leftShift(n), rightArithShift(n) — operate directly on the raw scaled bigint.

Vector Operations (main build only)

FixedPrecision.dot(a, b) → scalar dot product.
FixedPrecision.cross(a, b) → returns an array (cross product).

Method Chaining with Raw Values

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 chain

Type behavior:

  • string / number → decimal values, automatically scaled.
  • bigint → treated as already scaled (pre‑scaled). See below.
  • FixedPrecision → requires matching precision context (for scaled operations).

BigInt Warning

critical: bigint values 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.

Minimal Build

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.

Contributing

  1. Fork the repository.
  2. Create a new branch: git checkout -b feature-name.
  3. Commit your changes: git commit -m 'Add some feature'.
  4. Push to the branch: git push origin feature-name.
  5. Open a pull request.

License

Distributed under the MIT License. See the LICENSE file for more details.

About

Precise fixed-scale (8 decimal places) arithmetic for financial calculations.

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages