npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@neutrium/decimal

v2.2.0

Published

The Neutrium Decimal library.

Readme

@neutrium/decimal

Arbitrary-precision decimal arithmetic for JavaScript and TypeScript.

@neutrium/decimal provides configurable precision and rounding, scientific functions, comparisons, and formatting without the floating-point surprises of JavaScript numbers.

Try the interactive demo to explore the functionality and compare Decimal calculations with native JavaScript.

Installation

pnpm install @neutrium/decimal

The package has no runtime dependencies and includes JavaScript source maps, TypeScript declarations, and declaration maps.

Applications upgrading from 1.x should follow the 2.0 migration guide.

Quick start

import { Decimal } from '@neutrium/decimal';

// Decimal
const result = new Decimal('0.1').add('0.2');

result.toString();         // '0.3'

// Native JavaScript
const nativeResult = 0.1 + 0.2;
console.log(nativeResult); // 0.30000000000000004

Pass decimal values as strings when their exact value cannot be represented by a JavaScript number, particularly for long values and decimal fractions.

Feature tiers

Import from @neutrium/decimal by default to use the full API. If bundle size is a priority, choose the smallest entry point that contains the operations your application needs:

| Entry point | Includes | Minified size | Budget | | --- | --- | ---: | ---: | | @neutrium/decimal/core | Representation, parsing, comparison, predicates, and string/number formatting | 15.03 KiB | 20 KiB | | @neutrium/decimal/arithmetic | Core plus arithmetic, rounding, shifting, and fractions | 26.30 KiB | 32 KiB | | @neutrium/decimal/scientific | Arithmetic plus powers, logarithms, trigonometry, PI, and atan2 (the complete API) | 42.25 KiB | 50 KiB | | @neutrium/decimal | Alias for the scientific tier; retained for compatibility | 42.24 KiB | 50 KiB |

Sizes are measured from the browser smoke fixtures (minified ES2022 IIFE, uncompressed, including fixture code) and refreshed by pnpm run build. Actual application sizes depend on usage and bundler settings. Budgets are enforced by pnpm run test:package.

Each entry point exports a Decimal constructor and the shared configuration, error, input, rounding, modulo, and limits types. The core, arithmetic, and scientific constructors have independent configurations; the root and scientific entry points expose the same complete constructor.

Internally, the implementations form the inheritance chain DecimalLike → CoreDecimal → ArithmeticDecimal → ScientificDecimal. The scientific entry point exports its implementation as both Decimal and ScientificDecimal, and the root entry point re-exports the scientific API. Both names refer to the same constructor and configuration.

Values are interoperable across tiers, so a richer tier can consume a value directly without converting it to a string:

import { Decimal as CoreDecimal } from '@neutrium/decimal/core';
import { Decimal as ArithmeticDecimal } from '@neutrium/decimal/arithmetic';

const parsed = new CoreDecimal('9007199254740993.0000000000000000001');
const calculated = new ArithmeticDecimal(parsed).add(1);

Creating decimals

Create a decimal with new Decimal(value), where value is a string, number, bigint, or another Decimal.

new Decimal(42).toString();                         // '42'
new Decimal(9007199254740993n).toString();          // '9007199254740993'
new Decimal('503248.334').toString();               // '503248.334'
new Decimal('4.321e+4').toString();                 // '43210'
new Decimal('.5').toString();                       // '0.5'
new Decimal(Infinity).toString();                   // 'Infinity'
new Decimal(NaN).toString();                        // 'NaN'

The number of digits in value is not limited, except by JavaScript's maximum array size and, in practice, the processing time required.

Decimal strings may use fixed-point or exponential notation. Numeric separators are accepted between digits, and prefixed binary, octal, and hexadecimal strings are also supported.

In exponential notation, e or E defines a power-of-ten exponent for decimal values, while p or P defines a power-of-two exponent for binary, octal, or hexadecimal values.

new Decimal('0.046_875').toString();                // '0.046875'
new Decimal('0b101.1').toString();                  // '5.5'
new Decimal('0o10').toString();                     // '8'
new Decimal('0xff.8').toString();                   // '255.5'
new Decimal('0x1.8p-5').toString();                 // '0.046875'
new Decimal('0b1p2_0').toString();                  // '1048576'

Invalid values, including objects other than Decimal and malformed numeric strings, throw a DecimalError.

Independent constructors

A Decimal with an isolated configuration can be created using Decimal.clone(). A clone starts with the calling constructor's current settings and then applies the supplied overrides.

const Money = Decimal.clone({
  precision: 20,
  rounding: 'half-even'
});

const Measurements = Decimal.clone({ precision: 50 });

Money.config = { precision: 12 };

Money.config.precision;        // 12
Measurements.config.precision; // 50
Decimal.config.precision;      // 20 (unchanged default)

Decimal-returning operations, Decimal.PI, Decimal.atan2(), and toFraction() preserve constructors produced by Decimal.clone().

Configuration

Configure Decimal behaviour through the following properties.

| Property | Description | Type | Valid values | Default | | --- | --- | --- | --- | --- | | precision | The maximum number of significant digits in the result of an operation | integer | 1 to 1e9 | 20 | | maxPrefixedDigits | Limits the number of decimal coefficient digits created while converting binary, octal, or hexadecimal strings | integer | 1 to 1e9 | 1e6 | | maxOutputDigits | Limits the mantissa digits produced by string-formatting operations. It includes leading and padded zeros, but excludes the sign, decimal point, and scientific exponent suffix | integer | 1 to 1e9 | 1e6 | | rounding | The default rounding mode used when rounding an operation's result to the configured number of significant digits | RoundingMode | See rounding modes | 'half-up' | | modulo | The modulo mode used when calculating the modulus | ModuloMode | See modulo modes | 'down' | | toExpNeg | The negative exponent value at and below which toString() returns exponential notation | integer | -9e15 to 0 | -7 | | toExpPos | The positive exponent value at and above which toString() returns exponential notation | integer | 0 to 9e15 | 21 | | minE | The negative exponent limit, i.e. the exponent value below which underflow to zero occurs | integer | -9e15 to 0 | -9e15 | | maxE | The positive exponent limit, i.e. the exponent value above which overflow to Infinity occurs | integer | 0 to 9e15 | 9e15 |

Configuration is scoped to a Decimal constructor. Assign any subset of the properties to Decimal.config:

Decimal.config = {
  precision: 40,
  rounding: 'half-even',
  modulo: 'euclid',
  toExpNeg: -9,
  toExpPos: 30,
  minE: -1e6,
  maxE: 1e6
};

Calculation results are rounded to the configured number of significant digits where the operation requires rounding. minE and maxE control underflow to zero and overflow to Infinity.

Rounding modes

A rounding mode may be specified using the constructor configuration and occasionally as a function parameter. Supported rounding modes are listed below:

| Value | Behavior | | --- | --- | | 'up' | Away from zero | | 'down' | Toward zero | | 'ceil' | Toward positive Infinity | | 'floor' | Toward negative Infinity | | 'half-up' | To nearest; ties away from zero | | 'half-down' | To nearest; ties toward zero | | 'half-even' | To nearest; ties toward the even neighbor | | 'half-ceil' | To nearest; ties toward positive Infinity | | 'half-floor' | To nearest; ties toward negative Infinity |

Methods that accept an optional rounding mode use the constructor's configured mode when it is omitted.

Modulo modes

The modulo mode determines how the quotient is rounded before calculating remainder = dividend - divisor * quotient. It accepts every RoundingMode plus 'euclid'. Common modulo modes are listed below:

| Value | Remainder behavior | | ------------- | ------------------------------------------------------- | | 'down' | Same sign as the dividend; equivalent to JavaScript % | | 'floor' | Same sign as the divisor; equivalent to Python % | | 'half-even' | IEEE 754 remainder | | 'euclid' | Always non-negative |

API

Decimal instances provide the following categories of precision-preserving operations.

The DecimalValue type used below is string | number | bigint | DecimalLike. Unless noted otherwise, methods return a new Decimal and do not change the receiver.

Refer to the API reference for comprehensive library documentation, and specifically the Decimal class for details of the complete API.

Static methods

| Member | Description | | --- | --- | | Decimal.config | Get a readonly configuration snapshot or assign partial configuration. | | Decimal.clone(config?) | Create an independently configured Decimal constructor. | | Decimal.atan2(y, x) | Return the angle in radians from the positive x-axis to (x, y). | | Decimal.min(value, ...values) or Decimal.min(values) | Return the minimum using the receiving constructor. | | Decimal.max(value, ...values) or Decimal.max(values) | Return the maximum using the receiving constructor. | | Decimal.PI | Pi as a Decimal from the receiving constructor. | | Decimal.limits | Readonly public validation limits: maxDigits and maxExponent. |

Inspection methods

| Method | Returns | Description | | --- | --- | --- | | dp() | number | Number of decimal places, or NaN for a non-finite value. | | precision(includeTrailingZeros?) | number | Number of significant digits. Pass true to count trailing integer zeroes; only booleans are accepted. | | sign() | number | -1, -0, 0, 1, or NaN, preserving the sign of zero. |

Arithmetic and powers

| Method | Description | | --- | --- | | add(value) | Add value to the decimal value. | | sub(value) | Subtract value from the decimal value. | | mul(value) | Multiply the decimal by value. | | div(value) | Divide the decimal by value. | | divToInt(value) | Divide the decimal by value and truncate the quotient to an integer. | | mod(value) | Return the remainder after dividing the decimal by value using the configured modulo mode. | | abs() | Return the absolute value of the decimal. | | neg() | Invert the sign of the decimal. | | shift(places) | Shift the decimal point by a safe integer number of places. | | pow(value) | Raise the decimal to the power of value. | | sqrt() | Return the square root of the decimal. | | cbrt() | Return the cube root of the decimal. |

Exponential and trigonometric methods

All angles are expressed in radians.

| Method | Description | | --- | --- | | exp() | Return e raised to the value of the decimal. | | ln() | Return the natural logarithm of the decimal. | | log(base) | Return the logarithm of the decimal in the supplied base. | | sin(), cos(), tan() | Trigonometric functions. | | asin(), acos(), atan() | Inverse trigonometric functions. | | sinh(), cosh(), tanh() | Hyperbolic functions. | | asinh(), acosh(), atanh() | Inverse hyperbolic functions. |

The internal constant for PI contains approximately 1,000 decimal places. That limits the maximum useful precision of trigonometric and logarithmic calculations.

Minimum, maximum, and comparison

| Method | Returns | Description | | --- | --- | --- | | Decimal.min(value, ...values) or Decimal.min(values) | Decimal | Minimum of scalar arguments or a finite, non-empty iterable. | | Decimal.max(value, ...values) or Decimal.max(values) | Decimal | Maximum of scalar arguments or a finite, non-empty iterable. | | cmp(value) | number | -1, 0, 1, or NaN. | | eq(value) | boolean | Equal to. | | gt(value) | boolean | Greater than. | | gte(value) | boolean | Greater than or equal to. | | lt(value) | boolean | Less than. | | lte(value) | boolean | Less than or equal to. |

Decimal.min() and Decimal.max() accept either one or more scalar DecimalValue arguments or one finite, non-empty iterable. Arrays, sets, and generators are streamed without being spread into an argument list. Strings remain scalar decimal values rather than being treated as iterables.

Predicates

| Method | Description | | --- | --- | | isFinite() | Whether the value is finite. | | isInt() | Whether the value is an integer. | | isNaN() | Whether the value is NaN. | | isNeg() | Whether the value is negative, including negative zero. | | isPos() | Whether the value is positive, including positive zero. | | isZero() | Whether the value is positive or negative zero. | | isOdd() | Whether the value is an odd integer. | | isEven() | Whether the value is an even integer. |

Rounding

| Method | Description | | --- | --- | | ceil() | Round toward positive Infinity. | | floor() | Round toward negative Infinity. | | round() | Round to an integer using the configured rounding mode. | | trunc() | Truncate to an integer toward zero. | | toNearest(value?, rounding?) | Round to the nearest multiple of the magnitude of value; its sign is ignored and the value defaults to 1. |

Conversion and formatting

| Method | Returns | Description | | --- | --- | --- | | toDP(dp?, rounding?) | Decimal | Round to at most dp decimal places. With no dp, return an equal new Decimal. | | toSD(sd?, rounding?) | Decimal | Round to at most sd significant digits. Defaults to configured precision. | | toExponential(dp?, rounding?) | string | Exponential notation with optional decimal-place count. | | toFixed(dp?, rounding?) | string | Fixed-point notation with optional decimal-place count; preserves negative zero. | | toPrecision(sd?, rounding?) | string | Precision notation with optional significant-digit count. | | toFraction(maxDenominator?) | readonly [Decimal, Decimal] \| readonly [Decimal] | Numerator and denominator, or a one-item tuple for a non-finite value. | | toNumber() | number | Convert to a JavaScript number, preserving negative zero. | | toString() | string | Canonical string using the configured exponential thresholds. | | toValue() | string | Primitive string representation that preserves negative zero. | | valueOf() | string | Exact value used for default coercion; preserves negative zero. | | toJSON() | string | Exact JSON string value; preserves negative zero and non-finite values. |

Coercion and JSON

String coercion uses toString(), so it follows the configured exponential thresholds and renders negative zero as '0'. Default coercion uses valueOf() and therefore preserves negative zero. Numeric coercion uses toNumber() and can lose precision or overflow like any conversion to a JavaScript number.

const exact = new Decimal('9007199254740993');

String(exact);                      // '9007199254740993'
exact + '';                         // '9007199254740993'
Number(exact);                      // 9007199254740992 (precision is lost)
JSON.stringify({ exact });          // '{"exact":"9007199254740993"}'
JSON.stringify(new Decimal('-0'));  // '"-0"'
JSON.stringify(new Decimal(NaN));   // '"NaN"'

JSON serialisation intentionally produces strings rather than JSON numbers so that finite values retain all digits and -0, NaN, and infinities retain their Decimal representation.

The + operator uses default coercion and therefore concatenates Decimal values as strings. Other arithmetic operators and relational operators request numeric coercion and may lose precision. Use add, sub, mul, div, and the Decimal comparison methods for exact Decimal operations.

Errors

All input validation and configured-limit failures throw DecimalError with a stable code property:

| Code | Meaning | | --- | --- | | INVALID_ARGUMENT | A constructor or method argument is invalid. | | INVALID_CONFIGURATION | A configuration value or the configuration object is invalid. | | UNKNOWN_CONFIGURATION_KEY | A configuration property is not supported. | | PRECISION_LIMIT_EXCEEDED | A calculation requires more digits than a built-in constant provides. | | PREFIXED_EXPANSION_LIMIT_EXCEEDED | A prefixed number would exceed maxPrefixedDigits. | | OUTPUT_DIGIT_LIMIT_EXCEEDED | String output would exceed maxOutputDigits. |

For example:

import { Decimal, DecimalError } from '@neutrium/decimal';

try
{
  new Decimal('not-a-number');
}
catch (error)
{
  if (error instanceof DecimalError)
  {
    console.error(error.code); // 'INVALID_ARGUMENT'
  }
}

TypeScript

The package exports these public types:

import {
  Decimal,
  DecimalError,
  type DecimalConfig,
  type DecimalConfigInput,
  type DecimalConstructor,
  type DecimalErrorCode,
  type DecimalFraction,
  type DecimalLike,
  type DecimalLimits,
  type DecimalValue,
  type DecimalValueIterable,
  type ModuloMode,
  type RoundingMode
} from '@neutrium/decimal';

Decimal-returning methods return Decimal. Decimal.clone() returns an independently configured DecimalConstructor; values created by it preserve that constructor through subsequent Decimal-returning operations.

const Money: DecimalConstructor = Decimal.clone({ precision: 24 });
const amount: Decimal = new Money('10.00').mul(3).toDP(2);
const angle: Decimal = Money.atan2(1, 1);

Changelog

2.2.0 - 2026-09-07

  • Added feature-tier entry points backed by a shared core → arithmetic → scientific inheritance chain.
  • Added iterable support to Decimal.min() and Decimal.max().
  • Added an interactive precision demo and automated demo publishing alongside the API documentation.
  • Improved multiplication performance for large coefficients and expanded package and architecture validation.
  • Expanded CI coverage to Node.js 24.

2.1.1 — 2026-09-01

  • Moved API documentation generation into the GitHub Pages deployment workflow.
  • Fixed toFixed() so negative values rounded to zero retain their negative sign.

2.0.1 — 2026-09-01

  • Corrected README formatting.

2.0.0 — 2026-08-31

  • Refactored the public API and internal calculation architecture.
  • Added human-readable rounding and modulo modes, structured errors, immutable private state, and bigint input support.
  • Removed JavaScript floating-point dependence from trigonometric calculations and improved performance.
  • See the 2.0 migration guide for upgrade details.

License

This project is licensed under the MIT License (see the LICENSE file for details).

What this means

You are free to:

  • Use this library for personal or commercial purposes
  • Modify and distribute the code
  • Include it in other projects

Under the following conditions:

  • You must include the original license and copyright notice

Disclaimer

This library is provided "as is", without warranty of any kind. Use at your own risk.