@fex-to/precise-compact
v1.2.1
Published
Intl.NumberFormat wrapper that shows compact notation (K, M, thousand, million, लाख, 万) ONLY for exact numbers. Prevents precision loss - returns regular format (1,234) for approximations instead of misleading compact notation.
Maintainers
Readme
precise-compact
Intl.NumberFormat wrapper that shows compact notation ONLY for exact numbers
Problem: Native Intl.NumberFormat with compact notation shows 1234 as "1.2K" (loses precision)
Solution: This library shows "1K" or "1 thousand" for exact 1000, but keeps "1,234" for non-exact 1234
Supports words (thousand, тысяча, тисяча, लाख, 万, พัน) and all numbering systems (Western, Ukrainian, Indian, Chinese, Japanese, Korean, Thai, Arabic)
Features • Installation • Usage • API
✨ Features
- 🎯 No approximations — Shows compact (1.5K, "1.5 thousand") only for exact values. Returns regular format (1,234) for non-exact instead of misleading "1.2K"
- 📝 Word-based notation — Display "thousand", "million", "тысяча", "миллион", "тисяча", "мільйон" instead of K, M
- 🌏 Multiple numbering systems — Western (K, M, B, T), Indian (लाख, करोड़), Chinese/Japanese/Korean (万, 億), Thai (พัน, ล้าน), Arabic (ألف, مليون)
- 💱 Currency support — Works with all currencies: $1.5K, ₹1 लाख, ¥1万, €1 Tsd.
- 🚀 Zero dependencies — Uses native
Intl.NumberFormatAPI - ⚡ High performance — ~3.2M ops/sec with minimal 2% overhead
- 📦 Tiny & tree-shakeable — ESM/CJS with full TypeScript types
- ✅ 100% test coverage — 169 tests including non-Western locales
📦 Installation
npm install @fex-to/precise-compact🚀 Quick Start
import { PreciseCompact } from '@fex-to/precise-compact';
// Word-based notation (default: short forms like K, M)
const format = PreciseCompact({
locale: 'en-US',
compactDisplay: 'long', // 👈 Use words instead of letters
});
// ✅ Exact values → compact notation
format.format(1000); // "1 thousand"
format.format(1500); // "1.5 thousand"
format.format(2500000); // "2.5 million"
// ❌ Non-exact values → regular format (not "1.2K" which would be misleading)
format.format(1234); // "1,234" (not "1.2 thousand")💡 Usage
Localized Word Formats ⭐
// English words
const formatEN = PreciseCompact({ locale: 'en-US', compactDisplay: 'long' });
formatEN.format(1000); // "1 thousand"
formatEN.format(1000000); // "1 million"
formatEN.format(1000000000); // "1 billion"
// Russian words
const formatRU = PreciseCompact({ locale: 'ru-RU', compactDisplay: 'long' });
formatRU.format(1000); // "1 тысяча"
formatRU.format(1000000); // "1 миллион"
formatRU.format(1000000000); // "1 миллиард"
// German words
const formatDE = PreciseCompact({ locale: 'de-DE', compactDisplay: 'long' });
formatDE.format(1000); // "1 Tausend"
formatDE.format(1000000); // "1 Million"
// Ukrainian words
const formatUA = PreciseCompact({ locale: 'uk-UA', compactDisplay: 'long' });
formatUA.format(1000); // "1 тисяча"
formatUA.format(1000000); // "1 мільйон"
formatUA.format(1000000000); // "1 мільярд"
// Short forms (K, M, B, T)
const formatShort = PreciseCompact({ locale: 'en-US', compactDisplay: 'short' });
formatShort.format(1500); // "1.5K" (default behavior)Currency & Locales
const formatUSD = PreciseCompact({ locale: 'en-US', currency: 'USD' });
formatUSD.format(1500); // "$1.5K"
formatUSD.format(1234); // "$1,234.00"
const formatEUR = PreciseCompact({ locale: 'de-DE', currency: 'EUR' });
formatEUR.format(1000); // "1 Tsd. €"Non-Western Numbering Systems 🌏
// 🇮🇳 Indian numbering system (लाख = lakh = 100,000 | करोड़ = crore = 10,000,000)
const formatHI = PreciseCompact({ locale: 'hi-IN', compactDisplay: 'long' });
formatHI.format(100000); // "1 लाख"
formatHI.format(150000); // "1.5 लाख"
formatHI.format(10000000); // "1 करोड़"
// 🇨🇳 Chinese (万 = wan = 10,000 | 億 = yi = 100,000,000)
const formatZH = PreciseCompact({ locale: 'zh-CN' });
formatZH.format(10000); // "1万"
formatZH.format(100000000); // "1亿"
// 🇯🇵 Japanese (万 = man = 10,000 | 億 = oku = 100,000,000)
const formatJA = PreciseCompact({ locale: 'ja-JP' });
formatJA.format(10000); // "1万"
formatJA.format(100000000); // "1億"
// 🇰🇷 Korean (만 = man = 10,000 | 억 = eok = 100,000,000)
const formatKO = PreciseCompact({ locale: 'ko-KR' });
formatKO.format(10000); // "1만"
formatKO.format(100000000); // "1억"
// 🇹🇭 Thai (พัน = thousand | ล้าน = million)
const formatTH = PreciseCompact({ locale: 'th-TH', compactDisplay: 'long' });
formatTH.format(1000); // "1 พัน"
formatTH.format(1000000); // "1 ล้าน"
// 🇸🇦 Arabic (ألف = thousand | مليون = million)
const formatAR = PreciseCompact({ locale: 'ar-SA', compactDisplay: 'long' });
formatAR.format(1000); // "١ ألف"
formatAR.format(1000000); // "١ مليون"📖 API
PreciseCompact(options)
Creates a formatter instance.
Parameters:
| Option | Type | Default | Description |
| ---------------- | ------------------- | --------- | ------------------------------------------------------------------------ |
| locale | string | "en-US" | BCP 47 locale code (supports all Intl locales) |
| currency | string | — | ISO currency code (e.g., "USD", "EUR", "INR") |
| compactDisplay | "short" \| "long" | "short" | "long" for words (thousand, लाख), "short" for letters (K, M) |
Returns: { format(value: number): string }
🎯 How It Works
Exactness Check
A number is "exact" if it can be represented without approximation. Non-exact values fall back to regular format to avoid misleading compact notation.
(abs(value) × 10^decimals) % scale === 0Examples:
| Input | Output | Reason |
| --------- | ------------- | ------------------------------------------- |
| 1000 | "1K" | ✅ Exact: 1000 ÷ 1000 = 1 |
| 1500 | "1.5K" | ✅ Exact: 1500 ÷ 1000 = 1.5 (1 decimal) |
| 1234 | "1,234" | ❌ Not exact → regular format (not "1.2K") |
| 1000000 | "1M" | ✅ Exact: 1M ÷ 1M = 1 |
| 1230000 | "1.23M" | ✅ Exact: 1.23M with 2 decimals |
| 1234567 | "1,234,567" | ❌ Not exact → regular format (not "1.23M") |
Why? Native Intl.NumberFormat with notation: "compact" would show 1234 as "1.2K", losing precision. This library prevents that.
⚡ Performance
Benchmark (100,000 iterations):
| Implementation | Avg Time | Throughput | | ------------------ | -------- | ---------------- | | PreciseCompact | 31ms | 3.2M ops/sec | | Native compact | 30ms | 3.3M ops/sec | | Overhead | +1ms | 1.02× slower |
Minimal performance cost (2%) for exact number detection.
🌐 Browser & Node.js Support
Requires Intl.NumberFormat with compact notation support:
- ✅ Node.js 18.17+ or 20+
- ✅ Chrome 77+, Firefox 78+, Safari 14.1+, Edge 79+
- ✅ All modern browsers and runtimes (Deno, Bun, etc.)
📄 License
Made with ❤️ by fex-to
