ThisCalculator

Everyday

Decimal.js to Number: Safe Conversion Guide

Updated · Standard formula, shown below

Decimal.js to Number: Safe Conversion Guide

This guide explains how to convert a Decimal.js object to a native JavaScript number, what precision you lose, and when you should avoid the conversion altogether. You will learn the direct toNumber() method, why it can silently drop precision, and safer alternatives for rounding or string output.

Why Decimal.js Exists

JavaScript’s native Number type uses IEEE 754 double-precision floating point. That means it can only represent about 15–17 significant decimal digits accurately, and many decimal fractions (like 0.1) cannot be stored exactly. For financial calculations, scientific measurements, or any situation where rounding errors compound, this is a problem. Decimal.js is an arbitrary-precision library that stores numbers as strings of digits and performs arithmetic exactly, to a configurable number of decimal places. It is widely used in Node.js and browser applications where currency or precise measurements matter.

How to Convert a Decimal.js Object to a Number

The simplest and most direct way is the toNumber() method. Every Decimal.js instance has it.

const Decimal = require('decimal.js');
const d = new Decimal('123.456');
const n = d.toNumber(); // 123.456
console.log(typeof n); // "number"

This works for any Decimal value, including negative numbers, exponents, and values with many decimal places. The result is a standard JavaScript number, which you can then use in Math functions, array methods, or any API that expects a primitive number.

Precision Loss: The Hidden Danger

When you call toNumber(), the exact decimal value is rounded to the nearest representable double-precision binary number. This is not a bug—it is the definition of a JavaScript number. For example:

const d = new Decimal('0.1000000000000000000000001');
console.log(d.toNumber()); // 0.1

Notice the result is exactly 0.1, not the original 0.1000000000000000000000001. The extra digits are lost. If you later multiply that number by a large integer, the error can become visible. For instance, multiplying by 1e10 gives 1000000000, but the exact product would be 1000000000.000000001. This is why converting early defeats the purpose of using Decimal.js in the first place.

When to Use toNumber() vs. Keep as Decimal

There are only a few situations where converting to a number is appropriate:

  • Displaying a value that has few significant digits and you don’t care about the last few decimal places.
  • Passing a value to a library that only accepts native numbers, such as most charting libraries or WebGL APIs.
  • Performing a calculation that is inherently approximate, like a physics simulation where 15 digits of precision are already overkill.

In most other cases—especially financial calculations, exact comparisons, or any operation that will be followed by more arithmetic—you should keep the value as a Decimal and only convert at the very end, right before output.

Safer Alternatives: Rounding and String Output

If you need a number but want to control the precision, use toDecimalPlaces() or toSignificantDigits() before converting:

const d = new Decimal('123.456789');
const rounded = d.toDecimalPlaces(2).toNumber(); // 123.46

This rounds the decimal to the desired number of places first, so the conversion to a number loses less information. For display purposes, you can also use toString() or toFixed() to get a string, which avoids the binary rounding entirely:

const d = new Decimal('0.1');
console.log(d.toFixed(20)); // "0.10000000000000000000" (exact decimal representation)

Note that toFixed() returns a string, not a number. If you need a number, you must convert, but you can do so after rounding to an acceptable precision.

Common Mistakes and Misconceptions

One frequent mistake is assuming that Number(decimal) works the same as decimal.toNumber(). In most cases it does, because the valueOf() method of Decimal.js returns a number. But Number(decimal) can be misleading because it hides the fact that a conversion is happening. Always prefer the explicit toNumber() for clarity.

Another misconception is that parseFloat(decimal.toString()) is safer. It is not—it does the same binary rounding as toNumber(). There is no way to get an exact native number for a decimal that has more than 15 significant digits, because the native type simply cannot hold it.

Finally, be careful when comparing Decimal values to numbers. The expression new Decimal('0.1') === 0.1 is always false because they are different types. Use .equals() or compare after converting both to the same type.

Performance Considerations

Decimal.js is significantly slower than native number arithmetic because it operates on strings and does manual digit-by-digit calculations. If you are processing millions of values, converting them all to numbers and using native arithmetic may be faster, but you sacrifice precision. A common pattern is to do the heavy arithmetic with Decimal.js, then convert the final result to a number for display or storage. This gives you the best of both worlds: exact intermediate calculations and fast output.

Alternatives to Decimal.js

If you only need to avoid floating-point errors in a few simple operations, you might not need a full library. You can use integer arithmetic with a fixed scaling factor (e.g., store cents instead of dollars). For example, 0.1 + 0.2 becomes 10 + 20 = 30 cents, which is exact. This is a common technique in financial software.

Other arbitrary-precision libraries include big.js (smaller, simpler) and bignumber.js (more features). They all have similar toNumber() methods. The choice depends on your needs: Decimal.js has a configurable precision and a rich API, while big.js is minimal and fast.

Frequently Asked Questions

Does toNumber() round or truncate?

It rounds to the nearest representable binary number. This is not the same as decimal rounding. For example, new Decimal('0.1').toNumber() gives exactly 0.1, but internally the binary representation is slightly less than 0.1. When you print it, JavaScript shows 0.1 because it rounds to 15 significant digits for display.

Can I convert a huge Decimal like 1e100 to a number?

Yes, but you will lose precision. A number can represent up to about 1.8e308, so 1e100 is within range, but only the first 15-17 significant digits are kept. The rest are zeros. If you need the exact value, keep it as a Decimal.

Why does 0.1 + 0.2 = 0.30000000000000004 in JavaScript?

This is the classic floating-point error. Both 0.1 and 0.2 are stored as binary fractions that are slightly off. When added, the error becomes visible. Decimal.js avoids this by storing the exact decimal string and doing arithmetic on that.

Is there a way to get an exact number from a Decimal without rounding?

No. The native number type has a fixed precision. If the decimal has more than 15-17 significant digits, the conversion is lossy. The only way to keep exactness is to stay in the Decimal domain or use a string representation.

Should I always use Decimal.js for money calculations?

Yes, if you need exact decimal arithmetic. But you can also use integer cents. The key is to avoid floating-point arithmetic on money. If you do use Decimal.js, convert to a number only at the final output step, after rounding to the appropriate number of decimal places.

What is the difference between toNumber() and valueOf()?

For Decimal.js, they are identical—both return a native number. The valueOf() method is called implicitly when you use the object in a numeric context, like +decimal or decimal * 2. Using toNumber() is more explicit and self-documenting.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.