ProvenPin Install

bignumber.js precise decimals: working versions (tested 2026-10-15)

Do exact decimal math with bignumber.js: 0.1 + 0.2 === 0.3, configurable rounding modes, fixed-point formatting, numeric comparisons and a cart-total helper, all offline.

passed7 passed · 0 failed2026-10-15 22:58 UTC
Status
passed
Last passed
2026-10-15 22:58 UTC
Tests
7 / 7
Language
TypeScript
Runtime
node v22.23.3
pinned versions3
bignumber.js@11.1.5@types/node@26.6.5tsx@4.23.15
run it
npm install --no-audit --no-fund --ignore-scripts=false
npm test
test environment
node v22.23.3
linux 6.17.0-1022-azure
x64
runner: github-actions 38820570375

The code

Exactly the files that passed. Your agent can pull them with get_recipe("bignumber-js-precise-decimals") (install), or see them on GitHub.

package.json
{
  "name": "bignumber-js-precise-decimals",
  "private": true,
  "type": "module",
  "scripts": {
    "test": "node --import tsx --test test/*.test.ts"
  },
  "dependencies": {"bignumber.js":"11.1.5"},
  "devDependencies": {"@types/node":"26.6.5","tsx":"4.23.15"}
}
src/decimals.ts
import BigNumber from "bignumber.js";

BigNumber.config({ DECIMAL_PLACES: 20, ROUNDING_MODE: BigNumber.ROUND_HALF_UP });

export type RoundingMode =
  | "up"
  | "down"
  | "ceil"
  | "floor"
  | "half-up"
  | "half-down"
  | "half-even";

const ROUNDING: Record<RoundingMode, number> = {
  up: BigNumber.ROUND_UP,
  down: BigNumber.ROUND_DOWN,
  ceil: BigNumber.ROUND_CEIL,
  floor: BigNumber.ROUND_FLOOR,
  "half-up": BigNumber.ROUND_HALF_UP,
  "half-down": BigNumber.ROUND_HALF_DOWN,
  "half-even": BigNumber.ROUND_HALF_EVEN,
};

export type Money = string | number;

export function exact(value: Money): BigNumber {
  return new BigNumber(value);
}

export function add(a: Money, b: Money): BigNumber {
  return new BigNumber(a).plus(b);
}

export function multiply(a: Money, b: Money): BigNumber {
  return new BigNumber(a).times(b);
}

export function round(value: Money, places: number, mode: RoundingMode = "half-up"): BigNumber {
  return new BigNumber(value).decimalPlaces(places, ROUNDING[mode]);
}

export function formatFixed(value: Money, places: number, mode: RoundingMode = "half-up"): string {
  return new BigNumber(value).toFixed(places, ROUNDING[mode]);
}

export function compare(a: Money, b: Money): number {
  return new BigNumber(a).comparedTo(b);
}

export type CartLine = {
  name: string;
  price: Money;
  qty: number;
};

export function cartTotal(lines: CartLine[], taxRate: Money = "0"): string {
  const subtotal = lines.reduce(
    (acc, line) => acc.plus(new BigNumber(line.price).times(line.qty)),
    new BigNumber(0),
  );
  const tax = subtotal.times(taxRate);
  return subtotal.plus(tax).toFixed(2, BigNumber.ROUND_HALF_UP);
}
test/decimals.test.ts
import { test } from "node:test";
import assert from "node:assert/strict";
import {
  add,
  multiply,
  round,
  formatFixed,
  compare,
  cartTotal,
} from "../src/decimals.ts";

test("0.1 + 0.2 is exact", () => {
  assert.equal(add(0.1, 0.2).toFixed(), "0.3");
  assert.notEqual(0.1 + 0.2, 0.3);
  assert.equal(add("0.1", "0.2").toString(), "0.3");
});

test("multiply avoids binary float drift", () => {
  assert.equal(multiply("1.1", "1.1").toFixed(), "1.21");
  assert.equal(multiply(0.07, 100).toFixed(), "7");
});

test("honours the requested rounding mode", () => {
  assert.equal(round("2.345", 2, "half-up").toFixed(2), "2.35");
  assert.equal(round("2.345", 2, "half-even").toFixed(2), "2.34");
  assert.equal(round("2.355", 2, "half-even").toFixed(2), "2.36");
  assert.equal(round("2.341", 2, "down").toFixed(2), "2.34");
  assert.equal(round("2.341", 2, "up").toFixed(2), "2.35");
  assert.equal(round("-2.341", 2, "floor").toFixed(2), "-2.35");
  assert.equal(round("2.341", 2, "ceil").toFixed(2), "2.35");
});

test("fixed-point formatting", () => {
  assert.equal(formatFixed("1.005", 2, "half-up"), "1.01");
  assert.equal(formatFixed("1.005", 2, "half-even"), "1.00");
  assert.equal(formatFixed("1", 4), "1.0000");
  assert.equal(formatFixed("-0.5", 0, "half-up"), "-1");
});

test("numeric comparisons ignore formatting", () => {
  assert.equal(compare("0.1", "0.10"), 0);
  assert.equal(compare("10", "2"), 1);
  assert.equal(compare("2", "10"), -1);
  assert.equal(compare("0.30000000000000004", "0.3"), 1);
});

test("cart total applies tax and rounds to cents", () => {
  const cart = [
    { name: "Coffee", price: "2.50", qty: 3 },
    { name: "Bagel", price: "1.10", qty: 2 },
  ];
  assert.equal(cartTotal(cart), "9.70");
  assert.equal(cartTotal(cart, "0.08"), "10.48");
});

test("cart total is exact for values that drift in binary", () => {
  const cart = [
    { name: "A", price: "0.1", qty: 1 },
    { name: "B", price: "0.2", qty: 1 },
  ];
  assert.equal(cartTotal(cart), "0.30");
});

Connect to ProvenPin

Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.

terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp

Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.