Decimal.js - Documentation

Introduction

What is Decimal.js?

Decimal.js is a JavaScript library providing arbitrary-precision decimal arithmetic. Unlike JavaScript’s native Number type, which uses double-precision floating-point representation (IEEE 754), Decimal.js allows you to perform calculations with decimal numbers of any precision, avoiding the rounding errors and inaccuracies inherent in floating-point arithmetic. This is crucial for applications requiring exact decimal calculations, such as financial systems, accounting software, and scientific computations where precision is paramount. It handles numbers as strings, ensuring accurate representation and manipulation of decimal values.

Why use Decimal.js?

JavaScript’s built-in Number type suffers from well-known limitations when dealing with decimal numbers. Floating-point representation leads to rounding errors that can accumulate and produce incorrect results, particularly in calculations involving multiple operations or very small/large numbers. Decimal.js overcomes these limitations by providing:

Features and Benefits

Installation and Setup

Decimal.js can be installed via npm or yarn:

npm install decimal.js
# or
yarn add decimal.js

After installation, you can import and use it in your JavaScript code:

import Decimal from 'decimal.js';

// Create Decimal objects
let a = new Decimal(1.23456789);
let b = new Decimal('0.0001');

// Perform calculations
let sum = a.plus(b);
let product = a.times(b);

// Output the results (results are Decimal objects)
console.log(sum.toString()); // Output: 1.23466789
console.log(product.toString()); // Output: 0.000123456789

// Set precision globally
Decimal.set({ precision: 10 }); //Optional global setting.

//Alternatively, set precision for individual operations:
let result = new Decimal(1).dividedBy(3).toPrecision(3); // Output: 0.333 

For browser usage without a module bundler, you can include the Decimal.js script directly in your HTML file. Download the decimal.min.js file from the project’s website and add a <script> tag to your HTML:

<script src="decimal.min.js"></script>
<script>
  // Use Decimal.js here,  Decimal is now a global object
  let a = new Decimal(10);
  console.log(a.toString());
</script>

Remember to adjust the path to decimal.min.js as needed. The global object approach is less preferred, as it can lead to conflicts with other libraries using the same name.

Basic Usage

Creating Decimal Objects

Decimal objects are created using the Decimal constructor. The constructor accepts various input types:

import Decimal from 'decimal.js';

// Creating Decimal objects from different inputs
const a = new Decimal(123.456);            // From a Number
const b = new Decimal('78.90');          // From a String - Recommended for maximum precision
const c = new Decimal([1, 2, 3, 4, 5]);   // From an array of digits (represents 12345)
const d = new Decimal(a);                 // From another Decimal object (Creates a copy)

console.log(a.toString(), b.toString(), c.toString(), d.toString());

Arithmetic Operations (+, -, *, /)

Decimal.js provides methods for all basic arithmetic operations. These methods return new Decimal objects, leaving the original operands unchanged:

import Decimal from 'decimal.js';

const a = new Decimal('10.5');
const b = new Decimal('2.5');

const sum = a.plus(b);      // Addition
const difference = a.minus(b); // Subtraction
const product = a.times(b);    // Multiplication
const quotient = a.dividedBy(b); // Division

console.log(sum.toString(), difference.toString(), product.toString(), quotient.toString()); //Output: 13 8 26.25 4.2

These methods have chainable counterparts for improved readability (eg. add, sub, mul, div). Note that division by zero will throw an error.

Comparison Operations (==, !=, >, <, >=, <=)

Decimal.js offers methods for comparing Decimal objects. These methods return boolean values:

import Decimal from 'decimal.js';

const a = new Decimal('10');
const b = new Decimal('10.00');
const c = new Decimal('11');

console.log(a.equals(b)); // true - compares values, ignoring representation differences.
console.log(a.equals(c)); // false
console.log(a.greaterThan(c)); // false
console.log(a.lessThan(c));  // true
console.log(a.greaterThanOrEqualTo(b)); // true
console.log(a.lessThanOrEqualTo(b));  // true

//Inequality operators can also be used directly:
console.log(a.comparedTo(b) === 0); // true, equivalent to a.equals(b)
console.log(a.comparedTo(c) < 0);  // true, equivalent to a.lessThan(c)

The comparedTo method returns -1 if the current Decimal object is less than the argument, 0 if equal, and 1 if greater than the argument.

Rounding Modes

Decimal.js provides several rounding modes that control how results are rounded during calculations. These modes are specified using the rounding property in the configuration object (see Precision and Configuration below) or as an argument to specific methods like toDecimalPlaces.

The available rounding modes are:

import Decimal from 'decimal.js';

Decimal.set({ rounding: Decimal.ROUND_HALF_UP }); //Set global rounding mode

let num = new Decimal('2.5');
console.log(num.toDecimalPlaces(0).toString()); // Output: 3

Decimal.set({ rounding: Decimal.ROUND_HALF_EVEN }); //Set global rounding mode
num = new Decimal('2.5');
console.log(num.toDecimalPlaces(0).toString()); // Output: 2

num = new Decimal('3.5');
console.log(num.toDecimalPlaces(0).toString()); // Output: 4

Precision and Configuration

The precision of Decimal.js calculations can be controlled globally using Decimal.set(), or individually for specific operations.

import Decimal from 'decimal.js';

// Set global precision to 5 decimal places
Decimal.set({ precision: 5, rounding: Decimal.ROUND_HALF_UP });

const a = new Decimal('1.23456789');
console.log(a.toString()); // Output: 1.23457 (Rounded due to global precision and rounding mode)

// Local precision override:
console.log(a.toDecimalPlaces(10).toString()); // Output: 1.2345678900 (Overrides global precision)

//Changing global settings
Decimal.set({ precision: 10});
console.log(a.toString()); // Output: 1.2345678900

Advanced Operations

Modulo Operation (%)

The modulo operation, represented by the modulo method (or its shorter alias mod), returns the remainder after division.

import Decimal from 'decimal.js';

const a = new Decimal('10');
const b = new Decimal('3');

const remainder = a.modulo(b); // or a.mod(b)
console.log(remainder.toString()); // Output: 1

The result will always have the same sign as the divisor (b in this example). Division by zero will throw an error.

Exponentiation (**)

Exponentiation is performed using the pow method. The first argument is the base, and the second is the exponent.

import Decimal from 'decimal.js';

const base = new Decimal('2');
const exponent = new Decimal('5');

const result = base.pow(exponent);
console.log(result.toString()); // Output: 32

const negativeExponentResult = base.pow('-2');
console.log(negativeExponentResult.toString()); //Output: 0.25

Both base and exponent can be Decimal objects or numbers. Note that fractional exponents will be subject to rounding based on configured precision.

Square Root (sqrt)

The square root is calculated using the sqrt method.

import Decimal from 'decimal.js';

const num = new Decimal('16');
const root = num.sqrt();
console.log(root.toString()); // Output: 4

const num2 = new Decimal('2');
const root2 = num2.sqrt();
console.log(root2.toString()); // Output: 1.4142135623730951 (precision dependent)

The sqrt method will throw an error if the input is negative.

Absolute Value (abs)

The absolute value of a Decimal object is obtained using the abs method.

import Decimal from 'decimal.js';

const a = new Decimal('-10');
const b = new Decimal('5');

console.log(a.abs().toString()); // Output: 10
console.log(b.abs().toString()); // Output: 5

Decimal to Number Conversion

Converting a Decimal object to a JavaScript number is done using the toNumber() method. Be aware that this conversion can lead to loss of precision due to the limitations of JavaScript’s floating-point representation.

import Decimal from 'decimal.js';

const d = new Decimal('123.456789');
const n = d.toNumber();
console.log(n); // Output: 123.456789 (Might be slightly different due to floating point inaccuracies)

Number to Decimal Conversion

As described in the Basic Usage section, a Decimal object can be created directly from a JavaScript number using the constructor. However, keep in mind that any inherent precision loss in the original JavaScript number will be carried over. Using string representation is generally recommended for maximum accuracy when creating decimals from numbers.

import Decimal from 'decimal.js';

const n = 123.456789;
const d = new Decimal(n);       //Potentially lossy conversion
const d2 = new Decimal(n.toString()); //Lossless conversion, preferred method if precision is important.


console.log(d.toString(), d2.toString()); // Output: 123.456789  123.456789 (may differ slightly depending on JavaScript engine and number representation)

Using n.toString() ensures the number is converted to a string before creating the Decimal, preventing potential inaccuracies.

Mathematical Functions

Decimal.js provides several advanced mathematical functions that operate on Decimal objects. These functions generally maintain the library’s high precision, although results might be subject to minor rounding based on the configured precision. Remember to always check the Decimal.js documentation for the most up-to-date information on function specifics and potential limitations.

Trigonometric Functions (sin, cos, tan)

These functions calculate the trigonometric values (sine, cosine, tangent) of an angle given in radians.

import Decimal from 'decimal.js';

const angle = new Decimal(Math.PI / 4); // 45 degrees in radians

const sinValue = angle.sin();
const cosValue = angle.cos();
const tanValue = angle.tan();

console.log(sinValue.toString()); // Output:  0.7071067811865476
console.log(cosValue.toString()); // Output: 0.7071067811865476
console.log(tanValue.toString()); // Output: 1

The input angle should be a Decimal object. The output is a new Decimal object representing the trigonometric value.

Inverse Trigonometric Functions (asin, acos, atan)

These functions compute the inverse trigonometric functions (arcsine, arccosine, arctangent), returning the angle in radians as a Decimal object. The input should be a Decimal object within the valid range for each function (-1 to 1 for asin and acos, any value for atan).

import Decimal from 'decimal.js';

const value = new Decimal('0.7071067811865476');

const asinValue = value.asin();
const acosValue = value.acos();
const atanValue = value.atan();


console.log(asinValue.toString()); // Output:  0.7853981633974483
console.log(acosValue.toString()); // Output: 0.7853981633974483
console.log(atanValue.toString()); // Output: 0.615479708629172

Results are also Decimal objects. Note that the precision of the results depends on the library’s internal calculations and configured precision.

Exponential and Logarithmic Functions (exp, ln, log)

import Decimal from 'decimal.js';

const num = new Decimal('2');
const base = new Decimal(10);

const expValue = num.exp();
const lnValue = num.ln();
const logBase10Value = num.log(base);  //Log base 10
const logBase2Value = num.log(2);    //Log base 2


console.log(expValue.toString());     // Output: 7.38905609893065
console.log(lnValue.toString());      // Output: 0.6931471805599453
console.log(logBase10Value.toString()); // Output: 0.3010299956639812
console.log(logBase2Value.toString()); // Output: 1

Input and output are Decimal objects. Error handling for invalid inputs (e.g., ln of a non-positive number) should be considered.

Hyperbolic Functions

Decimal.js provides hyperbolic functions: sinh, cosh, tanh, asinh, acosh, and atanh. These functions mirror their trigonometric counterparts but operate on hyperbolic curves. Usage is analogous to the trigonometric functions described above, with input and output being Decimal objects.

import Decimal from 'decimal.js';

const num = new Decimal(1);

console.log(num.sinh().toString()); // Output: 1.1752011936438014
console.log(num.cosh().toString()); // Output: 1.5430806348152437
console.log(num.tanh().toString()); // Output: 0.7615941559557649

Remember to consult the official Decimal.js documentation for detailed information about the accuracy, range, and error handling of each function. Note that the results of these calculations can be sensitive to the configured precision. Higher precision settings will generally lead to more accurate results, but at the cost of increased computation time.

Comparison and Equality

Comparing Decimal Objects

Decimal.js provides several methods for comparing Decimal objects. The core method is comparedTo, which offers a numerical comparison:

import Decimal from 'decimal.js';

const a = new Decimal('10.5');
const b = new Decimal('10.500');
const c = new Decimal('11');

console.log(a.comparedTo(b)); // Output: 0 (a and b are equal)
console.log(a.comparedTo(c)); // Output: -1 (a is less than c)
console.log(c.comparedTo(a)); // Output: 1 (c is greater than a)

comparedTo returns:

Other comparison methods offer a more convenient, boolean-based comparison:

import Decimal from 'decimal.js';

const a = new Decimal('10.5');
const b = new Decimal('10.500');

console.log(a.equals(b));             // Output: true (equals ignores trailing zeros)
console.log(a.greaterThan(b));        // Output: false
console.log(a.lessThan(b));          // Output: false
console.log(a.greaterThanOrEqualTo(b));// Output: true
console.log(a.lessThanOrEqualTo(b));  // Output: true

Testing for Equality

When testing for equality, it’s crucial to understand how Decimal.js handles trailing zeros. The equals method provides a robust solution. It compares the numerical values, disregarding differences in the number of trailing zeros.

import Decimal from 'decimal.js';

const a = new Decimal('1.000');
const b = new Decimal('1');

console.log(a.equals(b)); // Output: true (Decimal.js considers them equal)
console.log(a.toString() === b.toString()); //Output: false (String comparison considers them different)

Directly comparing using the === operator on Decimal objects is generally not recommended for equality checks; instead, use the equals method.

Handling Precision Differences

Decimal.js’s equals method inherently handles precision differences when comparing numerical values. However, if you need to compare numbers based on a specific level of precision, you can use methods like toDecimalPlaces or toPrecision before comparison:

import Decimal from 'decimal.js';

const a = new Decimal('1.23456');
const b = new Decimal('1.23457');
const c = new Decimal('1.234567');

console.log(a.equals(c)); // Output: false (different values)


//Comparing to 4 decimal places
console.log(a.toDecimalPlaces(4).equals(c.toDecimalPlaces(4))); // Output: true (equal when rounded to 4 decimal places)


//Comparing using toPrecision:
console.log(a.toPrecision(5).equals(b.toPrecision(5))); //Output: false (different when rounded to 5 significant digits)
console.log(a.toPrecision(5).equals(c.toPrecision(5))); //Output: true (equal when rounded to 5 significant digits)

This approach allows you to define the level of precision required for your equality checks. Always consider the appropriate precision needed for the context of your application.

Error Handling

Common Errors and Exceptions

Decimal.js throws exceptions in specific situations to indicate errors during calculations. The most common errors include:

import Decimal from 'decimal.js';

try {
  const result = new Decimal(10).dividedBy(0);
  console.log(result);
} catch (e) {
  console.error("Error:", e.message); // Output: Error: Division by zero
}

try {
  const invalidDecimal = new Decimal('abc');
  console.log(invalidDecimal);
} catch (e) {
  console.error("Error:", e.message); //Output will vary depending on the Decimal.js version and browser
}

Always wrap potentially error-prone Decimal.js operations within try...catch blocks to handle exceptions gracefully.

Debugging Techniques

Debugging Decimal.js code often involves careful examination of inputs and outputs. Key debugging techniques include:

Best Practices for Error Prevention

To prevent errors and ensure accuracy:

By adhering to these best practices, you can significantly reduce errors and build robust applications using Decimal.js.

Performance Considerations

Decimal.js provides accurate decimal arithmetic, but its operations are generally slower than native JavaScript Number operations due to the overhead of arbitrary-precision calculations. Understanding performance considerations is crucial for building efficient applications.

Optimizing Decimal Operations

Several strategies can help optimize Decimal.js operations:

import Decimal from 'decimal.js';

// Inefficient (repeated object creation)
let sum = new Decimal(0);
for (let i = 0; i < 100000; i++) {
  sum = sum.plus(new Decimal(i));
}

// More efficient (reusing object)
let efficientSum = new Decimal(0);
for (let i = 0; i < 100000; i++) {
  efficientSum.add(i); //Using add directly
}

The second example is more efficient as it avoids repeatedly creating new Decimal objects.

Benchmarking and Tuning

To measure and tune the performance of your Decimal.js code:

Strategies for Large-Scale Computations

For large-scale computations with Decimal.js:

Remember that the choice of optimization strategies will depend heavily on the specific nature of your large-scale computation and the acceptable level of performance trade-offs. Profiling and benchmarking are essential for identifying the most effective optimizations.

API Reference

This section provides a comprehensive reference to the Decimal.js API. Due to the extensive nature of the API, providing a completely detailed explanation of every method and property within this response is impractical. This overview will cover the key aspects and provide examples, encouraging you to consult the official Decimal.js documentation for the most complete and up-to-date information.

Constructor and Methods

The core of Decimal.js is its constructor, Decimal(), and its numerous methods. The constructor creates a new Decimal object:

const myDecimal = new Decimal('123.45');

Numerous methods are available for arithmetic operations (plus, minus, times, dividedBy, modulo), comparison (equals, greaterThan, lessThan, etc.), mathematical functions (sin, cos, sqrt, ln, etc.), rounding and formatting (toDecimalPlaces, toPrecision, toFixed, toString), and more. These methods are chainable for improved readability and conciseness:

const result = new Decimal('10').plus(5).times(2).toDecimalPlaces(2);

Properties and Attributes

Decimal.js offers several properties, primarily for configuration and accessing internal information. Note that directly manipulating internal properties is generally discouraged and should be avoided unless you have a deep understanding of the library’s internal workings. Key properties include:

These properties are typically managed using the Decimal.set() method for configuration:

Decimal.set({ precision: 10, rounding: Decimal.ROUND_HALF_UP });

Detailed Explanation of Each Method

A comprehensive explanation of every method would be too extensive for this context. The official Decimal.js documentation is the definitive source for detailed explanations. However, we can briefly touch on some key method categories:

Examples of Each Method

Providing an example for every method would be excessively long. Instead, let’s illustrate a few important ones:

import Decimal from 'decimal.js';

// Arithmetic
const sum = new Decimal('2.5').plus('3.7');         //Addition - Output: 6.2
const diff = new Decimal(10).minus(3);             //Subtraction - Output: 7
const prod = new Decimal(4).times(5);               //Multiplication - Output: 20
const div = new Decimal(10).dividedBy(3);           //Division - Output: 3.3333333333333335 (Precision Dependent)
const mod = new Decimal(17).modulo(5);              //Modulo - Output: 2
const pow = new Decimal(2).pow(3);                   //Power - Output: 8


//Comparison
const isEqual = new Decimal('1.00').equals('1');    //Equality - Output: true
const greater = new Decimal(5).greaterThan(3);      //GreaterThan - Output: true

//Rounding and Formatting
const rounded = new Decimal('12.3456').toDecimalPlaces(2); //Output: 12.35
const precise = new Decimal('1234567').toPrecision(3);   //Output: 1.23e+6
const formatted = new Decimal('12.34').toFixed(1);      //Output: 12.3

//Mathematical function
const sqrtResult = new Decimal(16).sqrt();             //Output: 4
const lnResult = new Decimal(Math.E).ln();            //Output: 1

Refer to the official Decimal.js documentation for exhaustive method listings, detailed explanations, and further examples. Remember to install the library (npm install decimal.js) before running these code snippets.

Examples and Use Cases

Decimal.js excels in scenarios where precise decimal arithmetic is crucial, avoiding the pitfalls of JavaScript’s native floating-point numbers. Here are some compelling use cases:

Financial Calculations

Financial applications demand utmost accuracy. Even small rounding errors can accumulate and lead to significant discrepancies in balances, interest calculations, or currency conversions. Decimal.js provides a robust solution:

import Decimal from 'decimal.js';

// Calculating compound interest
const principal = new Decimal('1000');
const rate = new Decimal('0.05'); // 5% interest rate
const years = new Decimal('5');
const interest = principal.times(rate.times(years));

const totalAmount = principal.plus(interest);
console.log("Total amount after 5 years:", totalAmount.toFixed(2)); //Output: 1250.00

//Currency Conversion with precise exchange rates.
const amountInUSD = new Decimal('100');
const exchangeRate = new Decimal('0.85'); //USD to EUR
const amountInEUR = amountInUSD.times(exchangeRate);
console.log("Amount in EUR:", amountInEUR.toFixed(2)); //Output: 85.00

This example demonstrates how Decimal.js ensures precise interest calculations and currency conversions, preventing accumulated rounding errors.

Scientific Computations

Scientific computations frequently involve very small or very large numbers, where the limitations of floating-point arithmetic become apparent. Decimal.js enables accurate results even with extreme values:

import Decimal from 'decimal.js';

// Calculating Avogadro's number multiplied by a small factor:
const avogadro = new Decimal('6.02214076e+23'); // Avogadro's number
const smallFactor = new Decimal('1e-10');
const result = avogadro.times(smallFactor);
console.log("Result:", result.toString()); //Output will be precise

// Handling very small numbers in calculations involving exponents
const smallNumber = new Decimal('1e-20');
const exponent = new Decimal(10);
const poweredResult = smallNumber.pow(exponent);
console.log("Powered result:", poweredResult.toString()); //Output will maintain precision

In this example, Decimal.js precisely handles Avogadro’s number and a small factor without losing significant figures, a common issue with native floating point numbers.

Cryptocurrency Applications

Cryptocurrency transactions involve precise amounts of digital assets. Decimal.js is invaluable for accurate calculations of balances, transaction fees, and exchanges:

import Decimal from 'decimal.js';

// Calculating transaction fees:
const transactionAmount = new Decimal('1.23456789'); //BTC
const feeRate = new Decimal('0.001'); // 0.1% fee
const transactionFee = transactionAmount.times(feeRate);
console.log("Transaction fee:", transactionFee.toFixed(8)); //Output will be precise to 8 decimal places


//Exchange calculations using precise exchange rate
const btcAmount = new Decimal('0.1');
const ethExchangeRate = new Decimal('1600'); //BTC to ETH
const ethAmount = btcAmount.times(ethExchangeRate);
console.log("ETH amount:", ethAmount.toString()); //Output maintains precision

This example showcases how to accurately calculate transaction fees and handle cryptocurrency exchanges using Decimal.js, preserving the necessary level of precision for cryptocurrency transactions.

Data Analysis

Data analysis frequently involves calculations with decimal numbers, especially when dealing with financial, statistical, or scientific data. Maintaining accuracy is crucial for reliable results:

import Decimal from 'decimal.js';

//Calculating the average of a set of decimal values:
const values = [new Decimal('1.2'), new Decimal('2.5'), new Decimal('3.7'), new Decimal('4.1')];
let sum = new Decimal(0);
for (const val of values) {
    sum = sum.plus(val);
}
const average = sum.dividedBy(values.length);
console.log("Average:", average.toFixed(2)); //Output: 2.88


//Calculating Standard Deviation of a set of decimal values:  (requires additional helper functions beyond the scope of this example, but Decimal.js would be essential for accurate intermediate calculations)

Decimal.js ensures that aggregations like averages and statistical computations are performed with high accuracy, yielding reliable analytical results. While the standard deviation example is not fully elaborated here for brevity, it highlights the usefulness of the library in more complex data analysis tasks. You would use Decimal.js for precise calculations within the standard deviation formula.

Contributing to Decimal.js

Contributions to Decimal.js are welcome! This section outlines the process for contributing to the project.

Development Setup

  1. Fork the Repository: Fork the official Decimal.js repository on GitHub to your personal account.

  2. Clone Your Fork: Clone your forked repository to your local machine:

    git clone git@github.com:<your-username>/decimal.js.git
    cd decimal.js
  3. Install Dependencies: Install the necessary dependencies using npm or yarn:

    npm install
    # or
    yarn install
  4. Set up Testing: Decimal.js uses a comprehensive test suite. Ensure the tests run correctly before making any changes. Run the tests using:

    npm test
    # or
    yarn test

Coding Standards

Decimal.js follows specific coding standards to ensure consistency and readability. Key aspects include:

Before submitting a pull request, ensure your code conforms to these standards. You can use linters like ESLint to help enforce the style guide automatically.

Testing and Quality Assurance

Decimal.js has a thorough test suite. Before submitting any changes, ensure that all existing tests pass and that you’ve added new tests to cover your changes. Thorough testing is essential to maintain the library’s accuracy and reliability. The test suite uses tools like Jasmine and Karma. Familiarize yourself with the testing process to effectively contribute.

Submitting Pull Requests

  1. Create a Branch: Create a new branch for your changes from the main or master branch (depending on the repository’s main branch):

    git checkout -b <your-branch-name>
  2. Make Your Changes: Implement your changes, adhering to the coding standards and adding comprehensive tests.

  3. Commit Your Changes: Commit your changes with clear and concise commit messages:

    git add .
    git commit -m "Your descriptive commit message"
  4. Push Your Branch: Push your branch to your forked repository:

    git push origin <your-branch-name>
  5. Open a Pull Request: On GitHub, open a pull request from your branch to the main branch of the official Decimal.js repository. Provide a clear description of your changes and address any feedback from the maintainers.

Remember to follow the contribution guidelines provided in the official Decimal.js repository. The maintainers will review your pull request and provide feedback. Be prepared to address any comments or suggested improvements before your contribution is merged.