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

@ekaone/rounded

v1.0.0

Published

A lightweight, zero-dependency TypeScript rounding library for finance and payments.

Readme

@ekaone/rounded

Lightweight, zero-dependency rounding library for finance and payments.

Features

  • Half Up — standard receipt/POS rounding
  • Banker's Rounding — round half to even, reduces cumulative bias
  • Cash Rounding — nearest 0.05 / 0.10 / 0.25 etc.
  • Currency-aware — correct decimal precision per ISO 4217 code
  • Null-safe — invalid inputs return null, never throw
  • Float-safe — guards against 0.1 + 0.2 drift
  • Zero dependencies
  • Dual ESM + CJS output with full TypeScript types

Installation

npm install @ekaone/rounded
# or
yarn add @ekaone/rounded
# or
pnpm add @ekaone/rounded

Quick Start

Core rounding

import rounded from "@ekaone/rounded";

rounded(2.345)                         // → 2.35  (halfUp, 2dp default)
rounded(2.345, { precision: 2 })       // → 2.35
rounded(2.345, { method: "bankers" })  // → 2.34
rounded(NaN)                           // → null
rounded(null)                          // → null
rounded(Infinity)                      // → null

Cash rounding

rounded.cash(1.03)                      // → 1.05  (default nearest: 0.05)
rounded.cash(1.02)                      // → 1.00
rounded.cash(1.07, { nearest: 0.10 })  // → 1.10
rounded.cash(1.13, { nearest: 0.25 })  // → 1.25

Currency-aware rounding

rounded.currency(10.1265, "USD")                         // → 10.13
rounded.currency(1500.5,  "JPY")                         // → 1501
rounded.currency(10.1265, "KWD")                         // → 10.127
rounded.currency(10.125,  "USD", { method: "bankers" })  // → 10.12

API

rounded(value, options?) — default export

The main entry point. Rounds a number using the specified method and precision.

rounded(value: unknown, options?: RoundedOptions): number | null

| Parameter | Type | Default | Description | |---------------------|------------------|------------|-------------------------------| | value | unknown | — | The number to round | | options.precision | number | 2 | Number of decimal places | | options.method | RoundingMethod | "halfUp" | Rounding algorithm to use |

Returns null if value is NaN, null, undefined, Infinity, or -Infinity.

rounded(2.345)                                      // → 2.35
rounded(2.345, { precision: 4 })                    // → 2.345
rounded(2.345, { method: "bankers" })               // → 2.34
rounded(2.345, { precision: 2, method: "bankers" }) // → 2.34
rounded(NaN)                                        // → null
rounded(null)                                       // → null

rounded.cash(value, options?) — cash rounding

Rounds to the nearest physical denomination. Useful when a currency has no low-denomination coins (e.g. Sweden, Australia, New Zealand).

rounded.cash(value: number, options?: CashOptions): number | null

| Parameter | Type | Default | Description | |--------------------|----------|---------|---------------------------------------------| | value | number | — | The amount to round | | options.nearest | number | 0.05 | The denomination increment to round toward |

Returns null if value is invalid or if nearest is 0, negative, or invalid.

rounded.cash(1.03)                      // → 1.05
rounded.cash(1.02)                      // → 1.00
rounded.cash(1.07, { nearest: 0.10 })  // → 1.10
rounded.cash(1.12, { nearest: 0.25 })  // → 1.00
rounded.cash(1.13, { nearest: 0.25 })  // → 1.25
rounded.cash(1.49, { nearest: 1 })     // → 1.00
rounded.cash(1.50, { nearest: 1 })     // → 2.00
rounded.cash(NaN)                      // → null

rounded.currency(value, code, options?) — currency-aware rounding

Rounds a value to the correct number of decimal places for a given ISO 4217 currency code. Precision is looked up automatically from the built-in currency map.

rounded.currency(value: number, code: string, options?: CurrencyOptions): number | null

| Parameter | Type | Default | Description | |------------------|--------------------------|------------|-------------------------------------------| | value | number | — | The amount to round | | code | string | — | ISO 4217 currency code (case-insensitive) | | options.method | CurrencyRoundingMethod | "halfUp" | Rounding algorithm to use |

Returns null if value is invalid or if code is not a recognised currency.

rounded.currency(10.1265, "USD")                         // → 10.13   (2dp)
rounded.currency(10.1265, "usd")                         // → 10.13   (case-insensitive)
rounded.currency(1500.5,  "JPY")                         // → 1501    (0dp)
rounded.currency(10.1265, "KWD")                         // → 10.127  (3dp)
rounded.currency(10.125,  "USD", { method: "bankers" })  // → 10.12
rounded.currency(10.135,  "USD", { method: "bankers" })  // → 10.14
rounded.currency(10.5,    "XYZ")                         // → null    (unknown code)

Named exports — low-level functions

All core functions are also exported individually for direct use without the rounded wrapper.

import { halfUp, bankers, cash, currency, CURRENCY_PRECISION } from "@ekaone/rounded";

halfUp(value, precision)

halfUp(value: number, precision: number): number

Standard half-up rounding. Ties (x.5) always round toward positive infinity. Does not perform input validation — pass a clean number.

halfUp(2.345, 2)   // → 2.35
halfUp(2.344, 2)   // → 2.34
halfUp(1.005, 2)   // → 1.01  (float-safe)
halfUp(-2.345, 2)  // → -2.34 (toward +infinity)
halfUp(2.5, 0)     // → 3
halfUp(1.23456, 4) // → 1.2346

bankers(value, precision)

bankers(value: number, precision: number): number

Banker's rounding (round half to even). Ties round to whichever neighbour has an even last digit, eliminating upward bias across large transaction sets. Does not perform input validation.

bankers(2.345, 2)  // → 2.34  (4 is even — stay)
bankers(2.355, 2)  // → 2.36  (5 is odd  — round up to 6)
bankers(2.5, 0)    // → 2     (2 is even — stay)
bankers(3.5, 0)    // → 4     (4 is even — round up)
bankers(-2.5, 0)   // → -2    (-2 is even — stay)
bankers(-3.5, 0)   // → -4    (-4 is even — round up)

cash(value, options?)

See rounded.cash — this is the same function.

currency(value, code, options?)

See rounded.currency — this is the same function.

CURRENCY_PRECISION

A read-only record mapping uppercase ISO 4217 currency codes to their decimal precision.

import { CURRENCY_PRECISION } from "@ekaone/rounded";

CURRENCY_PRECISION["USD"]  // → 2
CURRENCY_PRECISION["JPY"]  // → 0
CURRENCY_PRECISION["KWD"]  // → 3

Types & Interfaces

All types are exported and fully available in TypeScript projects.

import type {
  RoundingMethod,
  RoundedOptions,
  CurrencyRoundingMethod,
  CurrencyOptions,
  CurrencyCode,
} from "@ekaone/rounded";

RoundingMethod

The rounding algorithm used by rounded().

type RoundingMethod = "halfUp" | "bankers";

| Value | Behaviour | |-------------|--------------------------------------------------| | "halfUp" | Ties round toward positive infinity (default) | | "bankers" | Ties round to nearest even digit (reduces bias) |


RoundedOptions

Options accepted by the main rounded() function.

interface RoundedOptions {
  /** Number of decimal places to round to. Defaults to 2. */
  precision?: number;

  /** Rounding algorithm. Defaults to "halfUp". */
  method?: RoundingMethod;
}

CurrencyRoundingMethod

The rounding algorithm used by currency(). Identical in values to RoundingMethod.

type CurrencyRoundingMethod = "halfUp" | "bankers";

CurrencyOptions

Options accepted by rounded.currency() and the named currency() export.

interface CurrencyOptions {
  /** Rounding algorithm. Defaults to "halfUp". */
  method?: CurrencyRoundingMethod;
}

CashOptions

Options accepted by rounded.cash() and the named cash() export.

interface CashOptions {
  /** The denomination to round toward. Must be a positive number. Defaults to 0.05. */
  nearest?: number;
}

CurrencyCode

A union type of all supported ISO 4217 currency code strings, derived directly from the CURRENCY_PRECISION map.

type CurrencyCode = keyof typeof CURRENCY_PRECISION;
// → "USD" | "EUR" | "JPY" | "KWD" | ... (30+ codes)

Use this to constrain function arguments in your own code:

import type { CurrencyCode } from "@ekaone/rounded";
import { currency } from "@ekaone/rounded";

function formatAmount(value: number, code: CurrencyCode): string {
  const result = currency(value, code);
  return result !== null ? `${code} ${result.toFixed(2)}` : "—";
}

Null-safety contract

Every public function that accepts user-facing input returns number | null. The library never throws. A null return always means the input was invalid.

| Input | Returns | |-------------------|----------| | NaN | null | | null | null | | undefined | null | | Infinity | null | | -Infinity | null | | Unknown currency | null | | nearest <= 0 | null | | Valid number | number |

The low-level named exports (halfUp, bankers) skip the null guard for performance. Only use them when you have already validated the input.


Supported currencies

| Decimals | Currencies | |----------|------------| | 2 | AED, AUD, BRL, CAD, CHF, CNY, EUR, GBP, HKD, ILS, INR, MXN, MYR, NOK, NZD, PHP, PLN, RON, SEK, SGD, THB, TRY, USD, ZAR | | 0 | BIF, CLP, DJF, GNF, IDR, JPY, KMF, KRW, MGA, PYG, RWF, UGX, VND, VUV, XAF, XOF, XPF | | 3 | BHD, IQD, JOD, KWD, LYD, OMR, TND |


Rounding methods explained

| Method | Tie behaviour | Best for | |------------|-------------------------------------|---------------------------------------------| | halfUp | 0.5 always rounds toward +∞ | Receipts, POS, customer-facing totals | | bankers | 0.5 rounds to nearest even digit | High-volume batch transactions, accounting |

Why Banker's Rounding? With halfUp, every tie rounds the same direction, introducing a small but consistent upward bias. Over thousands of transactions this accumulates. bankers distributes ties evenly between up and down, keeping running totals statistically neutral.


Float-safety

JavaScript's IEEE 754 floats produce surprising results like 1.005 * 100 = 100.49999..., which would cause 1.005 to incorrectly round down to 1.00. This library uses a string-based decimal shift strategy — converting via toPrecision(15) and manipulating digit positions as strings before any arithmetic — so tie values are always exact.

rounded(1.005)  // → 1.01  ✓  (naive Math.round(1.005 * 100) / 100 → 1.00 ✗)
rounded(1.045)  // → 1.05  ✓
rounded(1.055)  // → 1.06  ✓

License

MIT © Eka Prasetia

Links