# currency.js: integer-backed money arithmetic for JavaScript

> currency.js stores amounts as integers behind the scenes to avoid the float errors that make 2.51 + .01 return 2.5199999999999996. It installs in one npm command and formats per locale, but it is not a general-purpose decimal type.

**scurker/currency.js** — A javascript library for handling currencies

- Repository: https://github.com/scurker/currency.js
- Website: https://currency.js.org
- Stars: 3,376 · Forks: 146
- Language: JavaScript
- License: MIT
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/scurker-currency-js

## The floating point problem currency.js exists to avoid

JavaScript numbers are IEEE 754 doubles, so 2.51 + .01 evaluates to 2.5199999999999996 and 2.52 - .01 evaluates to 2.5100000000000002. The README shows both of these side by side with the library's answers, currency(2.51).add(.01) returning 2.52 and currency(2.52).subtract(.01) returning 2.51. The README links a talk by Bartek Szopka for the underlying explanation.

The intended audience is straightforward: front-end and Node developers who display prices, totals or line items and who have seen a cents value drift after a few operations. The library is not aimed at accounting ledgers, tax engines or anything that needs arbitrary precision. It is a small formatting and arithmetic helper that happens to get the common cases right.

## How the integer backing store and arithmetic methods work

The README states the library works with values as integers behind the scenes. That is the whole mechanism: an amount is scaled by the precision option and held as an integer, so addition and subtraction are integer operations rather than double operations. The README puts the ceiling explicitly, saying values should be less than 2^53 in cents, or 90,071,992,547,409.91.

Input is flexible. The README shows currency(123), currency(1.23), currency("1.23"), currency("$12.30") and passing an existing currency object, all producing a value. The fromCents option flips the interpretation, so currency(123, { fromCents: true }) is 1.23, and combining it with precision changes the scale, as in currency(123, { fromCents: true, precision: 3 }) giving 0.123.

The arithmetic surface is small and named: add, subtract, multiply, distribute. The distribute example is the interesting one, because it splits an amount into parts that sum back to the original: currency(1.12).distribute(5) returns [0.23, 0.23, 0.22, 0.22, 0.22]. That is the behaviour you want when splitting a bill, and it is not something you get by dividing and rounding each share independently.

Formatting is a separate step. format() applies the symbol, separator and decimal options, and the README shows currency("2,573,693.75").add("100,275.50").format() producing "$2,673,969.25". A custom format function can be supplied, receiving the currency object and the options object and expected to return a string.

## Installing currency.js and formatting a first amount

The README gives three installation routes. With npm, run the install command and the package lands in your dependencies. The package.json declares "main": "dist/currency.js", "module": "dist/currency.es.js" and "browser": "dist/currency.min.js", so bundlers pick the ES module build and script tags get the minified one.

```bash
npm install --save currency.js
```

Yarn users get the equivalent command, and there is a CDN path for pages without a build step. The README pins the CDN example to the 2.0.x line.

```html
<script src="https://unpkg.com/currency.js@~2.0.0/dist/currency.min.js"></script>
```

Once installed, the smallest useful program is an import plus one arithmetic chain. The default options conform to USD, so no configuration is needed for a dollar amount. The README's usage section shows the value forms and the add/subtract chains directly.

```js
currency(2.51).add(.01);       // 2.52
currency(2.52).subtract(.01);  // 2.51
```

The README also documents a next tag for unreleased commits, which is worth knowing about if you need a fix that has not shipped in a release.

```bash
npm install --save currency.js@next
```

For a non-USD locale, the README's euro example changes symbol, separator and decimal together. Note the order of the options: separator is the group divider and decimal is the fractional mark, which is the reverse of what a European reader might assume from the names alone.

```js
var euro = value => currency(value, { symbol: "€", separator: ".", decimal: "," });
euro("2.573.693,75").add("100.275,50").format();  // "€2.673.969,25"
```

For multiple currencies in one app, the README recommends factory functions rather than passing options at every call site, which keeps the symbol and precision decisions in one place.

## Where currency.js stops: precision, rounding and invalid input

The bound is the first real constraint. The README says the integer approach works for most reasonable values and sets the ceiling at 2^53 in cents. Above that, the integer backing store is no safer than a double, and the guarantee the library advertises no longer holds. If you are summing large aggregates, this is the number to check against your data.

Rounding is opt-in and narrow. The increment option rounds the display value to the nearest increment, with the README's example being currency(1.48, { increment: .05 }) giving 1.50. That covers nickel rounding. It does not give you banker's rounding, half-up versus half-even selection, or a documented policy for what happens at the exact midpoint of an increment. The README is silent on those cases.

Invalid input is permissive by default. errorOnInvalid defaults to false, so null or undefined does not throw unless you turn the option on. That is a deliberate choice, and it means a bug upstream can silently become a zero rather than an exception. Teams that want loud failures should set errorOnInvalid: true explicitly; the README does not describe what value an invalid input resolves to when the option is off.

There is also no division method in the documented API. You get add, subtract, multiply and distribute. If your calculation needs to divide an amount, you are back to working with the underlying value and deciding the rounding yourself, which is exactly the class of decision the library otherwise takes off your hands.

## currency.js vs dinero.js: different answers to the same question

The README's own Other Libraries list names accounting.js, dinero.js and walletjs as alternatives. The comparison worth making is with dinero.js, because the two take opposite architectural positions.

currency.js is a value wrapper with a small method set and a formatting layer, and its documented promise is size: about 1kb. dinero.js, as described in currency.js's own README listing, is a currency library in the same problem space. The practical difference for a reader is scope. currency.js documents symbol, separator, decimal, precision, pattern, negativePattern, increment, useVedic, fromCents and errorOnInvalid, and nothing about a currency registry or conversion rates. If your requirement is "show this number as dollars" or "add these line items", currency.js is the smaller dependency. If your requirement is "model an amount in one of many ISO currencies and convert between them", the option set here does not cover it, and you should be evaluating the alternatives rather than stretching this one.

accounting.js is listed as well, and it is the older formatting-first option. The distinction is that currency.js keeps an integer-backed value object rather than formatting a raw number, so arithmetic between two currency.js values does not go through a float at any point.

## Type definitions, licence and what an upgrade costs

The repository ships type definitions for two systems. package.json defines build steps named copy-typescript-definition and copy-flow-definition, which copy src/currency.d.ts and src/currency.js.flow into dist, and the test script runs test:typescript and test:flow alongside test:js. So TypeScript consumers get declarations from the published package rather than from a separate @types package, and Flow users are covered by the same release process.

The licence is MIT, declared in the repository's license file and referenced at the end of the README. MIT permits commercial use and modification provided the copyright notice and permission notice are retained; the repository does not add a separate patent grant or a contributor licence agreement that the README documents. That is the standard shape for a library of this size, and it is worth confirming with your own legal review rather than taking this summary as advice.

On upgrade cost, the facts are narrow. The most recent release is v2.0.4 from 2021-05-19, preceded by v2.0.3 and v2.0.2 in 2020. The repository is not archived, and the last push was on 2026-09-19. A repository that receives commits long after its last tagged release is a real pattern to weigh: fixes may exist on the default branch that are not in the version you install from npm. The README's own next tag exists for that reason, and installing currency.js@next is the documented way to pick up unreleased commits. The changelog.md file at the repository root is where release notes live; the README does not document a deprecation or migration policy for the 2.x line.

## Conclusion

Adopt currency.js when you need to add, subtract, multiply, distribute and format a single currency amount in a browser bundle of about 1kb, and when your values stay under 90,071,992,547,409.91 in cents. Skip it when you need exact decimal division, a full ISO 4217 currency registry, or exchange-rate conversion; dinero.js is the closer fit there. Before committing, verify three things in your own environment: that your amounts never exceed the 2^53 cent bound, that your locale's grouping matches the separator, decimal and useVedic options, and that your rounding rule is expressible through the increment option rather than a custom format function.

## FAQ

### Is currency.js a decimal.js replacement?

No. currency.js holds values as integers scaled by the precision option and caps usable values at 2^53 in cents, or 90,071,992,547,409.91, as the README states. The documented methods are add, subtract, multiply and distribute, with no division method and no arbitrary-precision path.

### What is currency.js and how do I install it?

It is a lightweight JavaScript library for working with currency values, built to work around floating point issues. The README gives npm install --save currency.js, yarn add currency.js, or a CDN script tag pointing at https://unpkg.com/currency.js@~2.0.0/dist/currency.min.js.

### How does formatting work in currency.js?

Calling format() applies the symbol, separator and decimal options, so currency("2,573,693.75").add("100,275.50").format() returns "$2,673,969.25". The pattern and negativePattern options use ! for the symbol and # for the amount, and a custom format function can replace the built-in behaviour.

### Does currency.js ship TypeScript types?

Yes. package.json includes copy-typescript-definition and copy-flow-definition build steps that copy src/currency.d.ts and src/currency.js.flow into dist, and the test script runs tsc against the test directory with --noEmit.

### How does currency.js handle rounding?

The increment option sets the closest increment the display value rounds to, with the README's example being currency(1.48, { increment: .05 }) returning 1.50. The README does not document a rounding mode setting or the behaviour at an exact midpoint.

## Sources

- [License: MIT](https://github.com/scurker/currency.js/blob/main/LICENSE)
- [Project website](https://currency.js.org)
- [README](https://github.com/scurker/currency.js/blob/main/README.md)
- [Releases](https://github.com/scurker/currency.js/releases)
- [scurker/currency.js on GitHub](https://github.com/scurker/currency.js)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/scurker-currency-js
