
Product
Introducing Webhook Events for Pull Request Scans
Add real-time Socket webhook events to your workflows to automatically receive pull request scan results and security alerts in real time.
@thesis-co/cent
Advanced tools
Arbitrary-precision currency library for TypeScript/JavaScript
cent
is a next-generation monetary math library designed to handle currencies with fixed precision, no matter how large the values or how many decimal places are required.
This makes cent
a good choice for accounting, F/X, trading, and cryptocurrency applications.
cent
?Popular libraries like dinero.js are built on JavaScript's Number
type, which has fundamental limitations:
Number
can only safely represent integers up to Number.MAX_SAFE_INTEGER
(2⁵³ - 1)Number
.cent
solves these problems with:
BigInt
for unlimited precision arithmeticimport { Money, Price } from '@thesis/cent'
// Creation
const usd = Money("$100.50")
const btc = Money("0.5 BTC")
// Arithmetic
const total = usd.add(Money("$25.25")) // $125.75
// Conversion with precision preservation
const price = new Price(Money("$50,000"), Money("1 BTC"))
const converted = usd.convert(price) // Exact BTC amount
// Allocation and distribution
const [first, second, third] = usd.allocate([1, 2, 1]) // [$25.13, $50.25, $25.12]
const [a, b, c] = usd.distribute(3) // [$33.50, $33.50, $33.50]
// Formatting
usd.toString({ locale: "en-US", compact: true }) // "$100.50"
btc.toString({ preferredUnit: "satoshi" }) // "50,000,000 sat"
Money()
and the Money
classThe Money()
factory function makes working with currencies simple:
import { Money } from '@thesis/cent'
// Parse currency symbols with auto-detection
const usd = Money('$1,234.56') // US Dollar: $1,234.56
const eur = Money('€1.234,56') // Euro (EU format): €1,234.56
const gbp = Money('£999.99') // British Pound: £999.99
const jpy = Money('¥50,000') // Japanese Yen: ¥50,000
// Parse currency codes (case insensitive)
const dollars = Money('USD 100.50')
const euros = Money('100.50 EUR')
// Parse cryptocurrency main units
const bitcoin = Money('₿2.5') // Bitcoin: 2.5 BTC
const ethereum = Money('ETH 10.123456') // Ethereum: 10.123456 ETH
// Parse cryptocurrency sub-units
const satoshis = Money('1000 sat') // 1000 satoshis = 0.00001000 BTC
const wei = Money('1000000 wei') // 1000000 wei = 0.000000000001 ETH
const gwei = Money('50 gwei') // 50 gwei = 0.00000005 ETH
// Parse with fractional unit symbols
const sats = Money('§10000') // 10000 satoshis = 0.0001 BTC
const cents = Money('¢50') // 50 cents = $0.50
const pence = Money('p75') // 75 pence = £0.75
// Supports negative amounts
const debt = Money('-$500.25')
const refund = Money('€-123.45')
// Financial precision - allows sub-cent amounts
const precise = Money('$100.12345') // 5 decimal places preserved
const microYen = Money('¥1000.001') // Sub-yen precision
A note on symbol priority: When symbols are shared (like $ for multiple currencies), the most traded currency takes priority based on global trading volume: $
→ USD, £
→ GBP, ¥
→ JPY. Use explicit currency codes for other currencies: AUD 100
, CAD 50
.
The Money
class provides safe monetary operations with automatic precision handling:
import { Money, EUR, USD } from '@thesis/cent'
// Create money instances
const euros = new Money({
asset: EUR,
amount: { amount: 50025n, decimals: 2n } // €500.25
})
const dollars = new Money({
asset: USD,
amount: { amount: 100000n, decimals: 2n } // $1,000.00
})
// Basic arithmetic (same currency only)
const sum = euros.add("€250.50")
console.log(sum.toString()) // "€750.75"
// Multiplication and division
const doubled = euros.multiply("2")
const half = euros.divide("2") // Only works with factors of 2 and 5
// Comparisons
console.log(euros.greaterThan(dollars)) // Error: Different currencies
console.log(euros.isPositive()) // true
console.log(euros.equals(euros)) // true
// Sorting arrays using compare method
const amounts = [Money("$100"), Money("$50"), Money("$200")]
const sorted = amounts.sort((a, b) => a.compare(b))
console.log(sorted.map(m => m.toString())) // ["$50.00", "$100.00", "$200.00"]
// Formatting options
console.log(euros.toString({ locale: 'de-DE' })) // "500,25 €"
console.log(euros.toString({ compact: true })) // "€500"
// Fractional unit symbol formatting
const btc = Money("0.01 BTC")
console.log(btc.toString({ preferredUnit: "sat" })) // "1,000,000 sats"
console.log(btc.toString({ preferredUnit: "sat", preferFractionalSymbol: true })) // "§1,000,000"
console.log(btc.toString({ preferredUnit: "sat", preferFractionalSymbol: true, compact: true })) // "§1M"
// Allocation and distribution
const budget = Money("$1000")
// Allocate proportionally by ratios
const [marketing, development, operations] = budget.allocate([2, 5, 3])
// Results: [$200, $500, $300] (2:5:3 ratio)
// Distribute evenly
const [alice, bob, charlie] = budget.distribute(3)
// Results: [$333.34, $333.33, $333.33] (remainder to first)
// Handle fractional units separately
const precise = Money("$100.00015")
const parts = precise.distribute(3, { distributeFractionalUnits: false })
// Results: [$33.33, $33.33, $33.34, $0.00015] (change separated)
cent
comes with two flavors of arbitrary-precision math utilities.
FixedPointNumber
is appropriate for financial applications that require
keeping track of "cents" or other fractional units of a currency. By
disallowing arbitrary division, fixed-point numbers make it difficult to
lose track of a fractional unit.
RationalNumber
is appropriate for wider arbitrary-precision math
applications.
import { FixedPoint, Rational } from '@thesis/cent'
// FixedPoint - Perfect for decimal numbers
const price = FixedPoint('1255.50') // Auto-detects 2 decimals
const rate = FixedPoint('0.875') // Auto-detects 3 decimals
// Percentage strings are automatically converted to decimals
const percentage = FixedPoint('51.5%') // Becomes 0.515 (auto-detects 3 decimals)
const tax = FixedPoint('8.25%') // Becomes 0.0825 (auto-detects 4 decimals)
// Arithmetic operations with automatic precision handling
const product = price.multiply("0.875")
console.log(product.toString()) // "1098.5625"
// Use percentage parsing in calculations
const totalWithTax = price.multiply(FixedPoint('8.25%'))
console.log(totalWithTax.toString()) // "103.5788" (8.25% of $1255.50)
// Precise division (only multiples of 2 and 5)
const half = price.divide("2")
const fifth = price.divide("5")
const tenth = price.divide("10")
// Comparison operations
console.log(price.greaterThan("0.875")) // true
console.log(price.lessThanOrEqual("0.875")) // false
// Also supports original constructor for explicit control
const explicit = new FixedPointNumber(125550n, 2n) // Same as FixedPoint('1255.50')
// Rational - fractions and exact arithmetic
// Create from fraction strings
const oneThird = Rational('1/3')
const twoFifths = Rational('2/5')
// Create from decimal strings (auto-converted to fractions)
const quarter = Rational('0.25') // Becomes 1/4
const decimal = Rational('0.125') // Becomes 1/8
// Create directly from bigint numerator and denominator
const pi = Rational(22n, 7n) // 22/7 approximation of π
const oneThird = Rational(1n, 3n) // 1/3
// Exact arithmetic
const sum = oneThird.add("2/5") // (1/3) + (2/5) = 11/15
console.log(sum.toString()) // "11/15"
const product = oneThird.multiply("2/5") // (1/3) * (2/5) = 2/15
console.log(product.toString()) // "2/15"
// Automatic simplification
const simplified = Rational('6/9')
console.log(simplified.toString()) // "2/3"
// Also supports original constructor
const explicit = new RationalNumber({ p: 1n, q: 10n }) // Same as Rational('1/10')
// Seamless conversion between types
const rational = Rational('3/8')
const decimalStr = rational.toDecimalString() // "0.375"
const fixedPoint = FixedPoint(decimalStr) // Auto-detects 3 decimals
console.log(fixedPoint.toString()) // "0.375"
cent
includes Price
and ExchangeRate
classes for representing price ratios between assets with mathematical operations.
ExchangeRate
has base/quote currency semantics, time-based operations, and everything you'd expect in a fintech app. It's appropriate rates retrieved from outside services like exchanges.
import { ExchangeRate, Money, USD, EUR, BTC, JPY } from '@thesis/cent'
// 1. Individual arguments with auto-timestamping
const usdEur = new ExchangeRate(USD, EUR, "1.08") // 1.08 EUR per USD
console.log(usdEur.toString()) // "1.08 €/$"
// 2. Individual arguments with custom timestamp and source
const btcUsd = new ExchangeRate(
BTC, USD, "50000", "1640995200", // 2022-01-01 timestamp
{ name: "Coinbase", priority: 1, reliability: 0.95 }
)
const eurJpy = new ExchangeRate({
baseCurrency: EUR,
quoteCurrency: JPY,
rate: "162.50",
timestamp: "1640995200",
source: { name: "ECB", priority: 1, reliability: 0.99 }
})
console.log(usdEur.baseCurrency.code) // "USD" (1 USD costs...)
console.log(usdEur.quoteCurrency.code) // "EUR" (...1.08 EUR)
// Rate inversion - swap base and quote
const eurUsd = usdEur.invert() // 1 EUR = 0.925925... USD
console.log(eurUsd.toString()) // "0.925925925925925926 $/@"
// Cross-currency calculations via multiplication
// EUR/USD × USD/JPY = EUR/JPY (USD cancels out)
const eurUsdRate = new ExchangeRate(EUR, USD, "1.0842")
const usdJpyRate = new ExchangeRate(USD, JPY, "149.85")
const eurJpyCalculated = eurUsdRate.multiply(usdJpyRate)
console.log(eurJpyCalculated.baseCurrency.code) // "EUR"
console.log(eurJpyCalculated.quoteCurrency.code) // "JPY"
console.log(eurJpyCalculated.rate.toString()) // "162.4673" (1.0842 × 149.85)
// Currency conversion with exchange rates
const dollars = Money("$100.00")
const euros = usdEur.convert(dollars)
console.log(euros.toString()) // "€108.00"
// Reverse conversion (automatic direction detection)
const backConverted = usdEur.convert(euros)
console.log(backConverted.toString()) // "$100.00"
// Automatic direction detection - same rate works both ways
const rate = new ExchangeRate(USD, EUR, "1.08") // 1 USD = 1.08 EUR
const usd100 = Money("$100")
const eur108 = rate.convert(usd100) // USD → EUR: $100 → €108
console.log(eur108.toString()) // "€108.00"
const convertBack = rate.convert(eur108) // EUR → USD: €108 → $100
console.log(convertBack.toString()) // "$100.00"
// Works with any amount and either currency in the rate
const moreEuros = Money("€540") // €540
const convertedDollars = rate.convert(moreEuros) // €540 ÷ 1.08 = $500
console.log(convertedDollars.toString()) // "$500.00"
// Exchange rate averaging for multiple sources
const rate1 = new ExchangeRate(USD, EUR, "1.07")
const rate2 = new ExchangeRate(USD, EUR, "1.09")
const averaged = new ExchangeRate(
ExchangeRate.average([rate1, rate2])
)
console.log(averaged.rate.toString()) // "1.080" (average of 1.07 and 1.09)
// Time-based operations
const currentRate = new ExchangeRate(USD, EUR, "1.08")
console.log(currentRate.isStale(300000)) // false (less than 5 minutes old)
// Formatting options
console.log(usdEur.toString()) // "1.08 €/$" (symbol format)
console.log(usdEur.toString({ format: "code" })) // "1.08 EUR/USD" (code format)
console.log(usdEur.toString({ format: "ratio" })) // "1 USD = 1.08 EUR" (ratio format)
// JSON serialization with BigInt support
const serialized = usdEur.toJSON()
const restored = ExchangeRate.fromJSON(serialized)
console.log(restored.equals(usdEur)) // true
// create bid/ask spreads for trading
const rate = new ExchangeRate(USD, EUR, "1.2000")
// apply spread using decimal string (2% spread)
const { bid, ask, mid } = rate.spread("0.02") // "2%" also works
console.log(bid.rate.toString()) // "1.1880" (1.2000 - 1% of 1.2000)
console.log(ask.rate.toString()) // "1.2120" (1.2000 + 1% of 1.2000)
console.log(mid.rate.toString()) // "1.2000" (original rate)
Price
is appropriate for arbitrary price pairs, covering the edge cases where ExchangeRate
might not be appropriate. It's easier to construct a new Price
and do math with it.
import { Price, USD, EUR, BTC, JPY } from '@thesis/cent'
// Define custom assets (for demonstration purposes)
const APPLE = {
name: 'Apple',
code: 'APPLE',
decimals: 0n,
symbol: '🍎'
}
const ORANGE = {
name: 'Orange',
code: 'ORANGE',
decimals: 0n,
symbol: '🍊'
}
// Create price ratios
const usdPerApple = new Price(
Money("$5.00"), // $5.00
{ asset: APPLE, amount: { amount: 1n, decimals: 0n } } // 1 apple (custom asset)
)
const applesPerBtc = new Price(
{ asset: APPLE, amount: { amount: 10000n, decimals: 0n } }, // 10,000 apples
Money("1 BTC") // 1.00000000 BTC
)
// Price-to-Price multiplication (assets must share a common unit)
// $5/apple × 10,000 apples/BTC = $50,000/BTC
// while this is fun, remember that there might not be apple-to-BTC liquidity 😉
const usdPerBtc = usdPerApple.multiply(applesPerBtc)
console.log(usdPerBtc.amounts[0].amount.amount) // 5000000n ($50,000.00)
// Price-to-Price division
// $50,000/BTC ÷ $5/apple = 10,000 apples/BTC
const calculatedApplesPerBtc = usdPerBtc.divide(usdPerApple)
// Scalar operations (multiply/divide by numbers)
const doubledPrice = usdPerApple.multiply("2") // $10.00/apple
const halfPrice = usdPerApple.divide("2") // $2.50/apple
// Convert to mathematical ratio
const ratio = usdPerApple.asRatio() // RationalNumber: 500/1
// Price operations validate shared assets
try {
const orangesPerBtc = new Price(
{ asset: ORANGE, amount: { amount: 5000n, decimals: 0n } },
Money("1 BTC")
)
usdPerApple.multiply(orangesPerBtc) // Error: no shared asset!
} catch (error) {
console.log(error.message)
// "Cannot multiply prices: no shared asset found between US Dollar/Apple and Orange/Bitcoin"
}
cent
includes a PriceRange
class for representing and manipulating price ranges with precision. Perfect for e-commerce filters, pricing strategies, and financial analysis.
import { PriceRange, Money, USD, EUR } from '@thesis/cent'
// Create ranges from strings
const range1 = PriceRange("$50 - $100")
const range2 = PriceRange("$50-100") // Compact format
const range3 = PriceRange("€25 - €75")
// Create from Money instances
const range4 = PriceRange(Money("$50"), Money("$100"))
// Mixed creation
const range5 = PriceRange("$50", Money("$100"))
console.log(range1.min.toString()) // "$50.00"
console.log(range1.max.toString()) // "$100.00"
console.log(range1.span.toString()) // "$50.00" (difference)
console.log(range1.midpoint.toString()) // "$75.00" (precise midpoint)
// Range operations and queries
console.log(range1.contains(Money("$75"))) // true
console.log(range1.contains("$25")) // false
console.log(range1.isAbove(Money("$40"))) // true (entire range above $40)
console.log(range1.isBelow(Money("$120"))) // true (entire range below $120)
// Range mathematics
const range6 = PriceRange("$80 - $150")
console.log(range1.overlaps(range6)) // true
const intersection = range1.intersect(range6)
console.log(intersection?.toString()) // "$80.00 - $100.00"
const union = range1.union(range6)
console.log(union.toString()) // "$50.00 - $150.00"
// Split ranges into equal parts
const parts = range1.split(3)
console.log(parts[0].toString()) // "$50.00 - $66.67"
console.log(parts[1].toString()) // "$66.67 - $83.33"
console.log(parts[2].toString()) // "$83.33 - $100.00"
// Static factory methods for common patterns
const underRange = PriceRange.under(Money("$100")) // "$0.00 - $100.00"
const overRange = PriceRange.over(Money("$50"), Money("$500")) // "$50.00 - $500.00"
const aroundRange = PriceRange.around(Money("$100"), "10%") // "$90.00 - $110.00"
// Create price buckets for filters
const buckets = PriceRange.createBuckets(Money("$0"), Money("$500"), 5)
buckets.forEach((bucket, i) => {
console.log(`Bucket ${i + 1}: ${bucket.toString()}`)
})
// Bucket 1: $0.00 - $100.00
// Bucket 2: $100.00 - $200.00
// Bucket 3: $200.00 - $300.00
// Bucket 4: $300.00 - $400.00
// Bucket 5: $400.00 - $500.00
// Display formatting options
console.log(range1.toString()) // "$50.00 - $100.00" (default)
console.log(range1.toString({ format: "compact" })) // "$50-100"
console.log(range1.toString({ format: "from" })) // "From $50.00"
console.log(range1.toString({ format: "upTo" })) // "Up to $100.00"
console.log(range1.toString({ format: "range" })) // "$50.00 to $100.00"
console.log(range1.toString({ format: "between" })) // "Between $50.00 and $100.00"
// Localized formatting
const eurRange = PriceRange("€50 - €100")
console.log(eurRange.toString({ locale: "de-DE" })) // "50,00 € - 100,00 €"
// Large ranges with compact notation
const largeRange = PriceRange("$1000000 - $5000000")
console.log(largeRange.toString({ compact: true })) // "$1M - $5M"
// Currency conversion
const exchangeRate = new ExchangeRate(USD, EUR, "0.85")
const convertedRange = range1.convert(exchangeRate)
console.log(convertedRange.toString()) // "€42.50 - €85.00"
// E-commerce product filtering
const products = [
{ name: "Budget Widget", price: Money("$45") },
{ name: "Standard Widget", price: Money("$75") },
{ name: "Premium Widget", price: Money("$125") },
{ name: "Deluxe Widget", price: Money("$95") }
]
const priceFilter = PriceRange("$50 - $100")
const affordableProducts = products.filter(product =>
priceFilter.contains(product.price)
)
console.log(affordableProducts.map(p => p.name))
// ["Standard Widget", "Deluxe Widget"]
// JSON serialization for APIs and storage
const serialized = range1.toJSON()
console.log(JSON.stringify(serialized))
const restored = PriceRange.fromJSON(serialized)
console.log(restored.equals(range1)) // true
// Cryptocurrency ranges with full precision
const btcRange = PriceRange("₿0.001 - ₿0.01")
console.log(btcRange.contains(Money("₿0.005"))) // true
console.log(btcRange.toString({ preferredUnit: "sat" })) // "100,000 sats - 1,000,000 sats"
cent
includes comprehensive currency metadata for accurate formatting:
import { USD, EUR, BTC, ETH, JPY } from '@thesis/cent'
// Traditional currencies
console.log(USD.decimals) // 2n
console.log(USD.symbol) // "$"
console.log(USD.fractionalUnit) // "cent"
// Cryptocurrencies with high precision
console.log(BTC.decimals) // 8n
console.log(BTC.fractionalUnit) // Complex object with multiple units
// Currencies with no decimals
console.log(JPY.decimals) // 0n
Safe serialization for APIs and storage:
const money = Money("$1,234,567,890,123.45")
// serialize (BigInt becomes string)
const json = money.toJSON()
console.log(JSON.stringify(json))
// {"asset":{"name":"United States dollar","code":"USD","decimals":"2","symbol":"$"},"amount":"1234567890123.45"}
// Deserialize
const restored = Money.fromJSON(json)
console.log(restored.equals(money)) // true
// FixedPointNumber also serializes as decimal strings preserving trailing zeros
const fp = FixedPoint("12.34500")
console.log(JSON.stringify(fp)) // "12.34500"
const restoredFp = FixedPointNumber.fromJSON("12.34500")
console.log(restoredFp.equals(fp)) // true
cent
automatically handles different precisions:
// different decimal places are automatically normalized
const fp1 = FixedPoint("10.0") // 1 decimal
const fp2 = FixedPoint("5.00") // 2 decimals
const sum = fp1.add("5.00") // Normalized to 2 decimals
console.log(sum.toString()) // "15.00"
Unlike floating-point arithmetic, cent
ensures exact division results:
const number = FixedPoint("100") // 100
console.log(number.divide("2").toString()) // "50.0"
console.log(number.divide("4").toString()) // "25.00"
console.log(number.divide("5").toString()) // "20.0"
console.log(number.divide("10").toString()) // "10.0"
// throws an exception (3 cannot be represented exactly in decimal)
try {
number.divide("3")
} catch (error) {
console.log(error.message) // "divisor must be composed only of factors of 2 and 5"
}
If you need division that would break out of what's possible to represent in
fixed point, you can mix FixedPointNumber
and RationalNumber
.
Rational("1/3").multiply(FixedPoint("100"))
// handle large transfers with perfect precision
const wireTransfer = Money("$9,999,999,999.99")
const fee = wireTransfer.multiply("0.005") // 0.5% fee
const afterFee = wireTransfer.subtract(fee)
// handle Bitcoin with satoshi and sub-satoshi precision
const satoshiAmount = Money("1 BTC")
console.log(satoshiAmount.toString({ preferredUnit: 'satoshi' }))
// "100,000,000 satoshis"
satoshiAmount.equals(Money("100000000 sat")) // true
// ethereum with wei precision (18 decimals)
const weiAmount = Money("1 ETH")
// Also supports original constructor for explicit control
const explicit = new Money({
asset: ETH,
amount: { amount: 1000000000000000000n, decimals: 18n } // Same as Money("1 ETH")
})
weiAmount.equals(Money("Ξ1.0")) // true
// Allocate amounts without losing precision
const revenue = Money("$12,345.67")
// Proportional allocation by department budgets
const [marketing, engineering, sales, operations] = revenue.allocate([2, 5, 2, 1])
// Results: [$2,469.13, $6,172.84, $2,469.13, $1,234.57] (2:5:2:1 ratio)
// Even distribution among team members
const bonus = Money("$10,000")
const [alice, bob, charlie] = bonus.distribute(3)
// Results: [$3,333.34, $3,333.33, $3,333.33] (remainder to first recipient)
// Handle fractional units for precision accounting
const preciseAmount = Money("$1,000.00123") // High-precision amount
const parts = preciseAmount.allocate([1, 1, 1], { distributeFractionalUnits: false })
// Results: [$333.33, $333.33, $333.34, $0.00123]
// Main allocations clean, fractional $0.00123 can go to a separate ledger
// Traditional concretization for currency sub-units
const [main, change] = preciseAmount.concretize()
console.log(main.toString()) // "$1,000.00" (standard currency precision)
console.log(change.toString()) // "$0.00123" (sub-unit precision)
Money()
- Parse currency strings with intelligent format detection
Money(str)
- Parse currency strings with symbols, codes, and crypto units
Money('$100.50')
, Money('€1.234,56')
, Money('£999')
Money('USD 100')
, Money('100.50 EUR')
(case insensitive)Money('₿2.5')
, Money('ETH 10.123456')
Money('1000 sat')
, Money('50 gwei')
, Money('1000000 wei')
Money('-$500')
, Money('$-123.45')
Money('$100.12345')
(preserves exact precision)Money(balance)
- Create from AssetAmount object (original constructor)FixedPoint()
- Create fixed-point numbers with ease
FixedPoint(str)
- Parse decimal string, auto-detect precision (e.g., FixedPoint('123.45')
)FixedPoint(percentage)
- Parse percentage string, auto-convert to decimal (e.g., FixedPoint('51.5%')
→ 0.515
)FixedPoint(fixedPoint)
- Copy/clone existing FixedPoint object (e.g., FixedPoint(existing)
)FixedPoint(amount, decimals)
- Create from bigint values (e.g., FixedPoint(12345n, 2n)
)Rational()
- Create rational numbers from strings, bigints, or objects
Rational(str)
- Parse fraction (e.g., Rational('22/7')
) or decimal (e.g., Rational('0.125')
)Rational(p, q)
- Create from bigint numerator and denominator (e.g., Rational(22n, 7n)
)Rational(ratio)
- Create from Ratio object (e.g., Rational({ p: 1n, q: 3n })
)PriceRange()
- Create price ranges with intelligent parsing
PriceRange(str)
- Parse range strings (e.g., PriceRange('$50 - $100')
, PriceRange('$50-100')
)PriceRange(min, max)
- Create from Money instances or strings (e.g., PriceRange(Money('$50'), '$100')
)Money
Arithmetic Operations (add/subtract accept Money objects or currency strings):
add(other)
- Add money amounts (same currency)subtract(other)
- Subtract money amounts (same currency)multiply(scalar)
- Multiply by number, FixedPoint, or stringabsolute()
- Get absolute valuenegate()
- Flip sign (multiply by -1)Allocation & Distribution:
allocate(ratios, options?)
- Split proportionally by ratios with optional fractional unit separationdistribute(parts, options?)
- Split evenly into N parts with optional fractional unit separationconcretize()
- Split into concrete amount and changeComparison Methods (accept Money objects or currency strings):
compare(other)
- Compare values: returns -1 if less, 0 if equal, 1 if greaterequals(other)
- Check equalitylessThan(other)
- Less than comparisongreaterThan(other)
- Greater than comparisonlessThanOrEqual(other)
- Less than or equal comparisongreaterThanOrEqual(other)
- Greater than or equal comparisonmax(other | others[])
- Return maximum valuemin(other | others[])
- Return minimum valueState Checks:
isZero()
- Check if amount is zeroisPositive()
- Check if amount is positiveisNegative()
- Check if amount is negativehasChange()
- Check if has fractional parthasSubUnits()
- Check if has sub-units beyond currency precisionConversion & Formatting:
convert(price)
- Convert to another currency using price/exchange ratetoString(options)
- Format for display with locale, precision, and unit optionstoJSON()
- Serialize to JSONfromJSON(json)
- Deserialize from JSONFixedPointNumber
Arithmetic Operations (accepts FixedPoint objects or string arguments):
add(other)
- Additionsubtract(other)
- Subtractionmultiply(other)
- Multiplicationdivide(other)
- Safe divisionnormalize(target)
- Change decimal precisionComparison Methods (accepts FixedPoint objects or string arguments):
equals(other)
- Equality checkgreaterThan(other)
- Greater than comparisonlessThan(other)
- Less than comparisonmax(other | others[])
- Return maximum valuemin(other | others[])
- Return minimum valueUtility Methods:
toString()
- DecimalString representationparseString(str, decimals)
- Parse from string with explicit decimalsfromDecimalString(str)
- Parse from DecimalString with auto-detected decimalsRationalNumber
Arithmetic Operations (accepts Ratio objects, fraction strings, or decimal strings):
add(other)
- Exact additionsubtract(other)
- Exact subtractionmultiply(other)
- Exact multiplicationdivide(other)
- Exact divisionComparison Methods (accepts Ratio objects, fraction strings, or decimal strings):
equals(other)
- Equality checkgreaterThan(other)
- Greater than comparisonlessThan(other)
- Less than comparisonmax(other | others[])
- Return maximum valuemin(other | others[])
- Return minimum valueUtility Methods:
simplify()
- Reduce to lowest termstoString()
- Convert to simplified "p/q" string formattoDecimalString(precision?)
- Convert to DecimalString (default 50 digits)toFixedPoint()
- Convert to decimal (when possible)Price
multiply(scalar | Price)
- Scalar multiplication or Price-to-Price multiplicationdivide(scalar | Price)
- Scalar division or Price-to-Price divisionasRatio()
- Convert to RationalNumber ratioinvert()
- Swap numerator and denominatorequals(other)
- Check equality (including time for timed prices)toExchangeRate(options?)
- Convert to ExchangeRate with configurable precision and base currency selectionExchangeRate
Constructor Overloads:
new ExchangeRate(data)
- Create from ExchangeRateData objectnew ExchangeRate(baseCurrency, quoteCurrency, rate, timestamp?, source?)
- Create from individual argumentsExchange Rate Specific:
multiply(scalar | ExchangeRate)
- Scalar multiplication or cross-currency rate calculationdivide(scalar | ExchangeRate)
- Scalar division or rate divisioninvert()
- Swap base and quote currencies (1/rate)convert(money)
- Convert Money between currencies (automatic direction detection)isStale(thresholdMs)
- Check if rate is older than thresholdtoString(options?)
- Format as "rate quote/base" with symbol, code, or ratio formatstoJSON()
- Serialize to JSON with BigInt string conversionfromJSON(json)
- Deserialize from JSONaverage(rates[])
- Static method to average multiple ratesfromPrice(price, options)
- Static method to create ExchangeRate from Price with configurable precisionPriceRange
Properties:
min
- Minimum price (Money instance)max
- Maximum price (Money instance)span
- Difference between max and min (Money instance)midpoint
- Precise midpoint of the range (Money instance)isEmpty
- True if min equals maxcurrency
- Currency of the rangeRange Operations:
contains(price)
- Check if price is within range (inclusive)isAbove(price)
- Check if entire range is above a priceisBelow(price)
- Check if entire range is below a priceoverlaps(other)
- Check if ranges overlapintersect(other)
- Get intersection range (or null)union(other)
- Get union rangesplit(parts)
- Split into N equal partsConversion & Formatting:
convert(exchangeRate)
- Convert to different currencytoString(options?)
- Format for display with multiple format stylestoJSON(options?)
- Serialize to JSONfromJSON(json)
- Deserialize from JSON (static)equals(other)
- Check equalityStatic Factory Methods:
under(max)
- Create range from zero to maxover(min, max)
- Create range from min to maxbetween(min, max)
- Alias for constructoraround(basePrice, percentage)
- Create range around price with margincreateBuckets(min, max, count)
- Create N equal price bucketsFeature | cent | dinero.js |
---|---|---|
Precision | Arbitrary (BigInt) | Limited (Number) |
Max Value | Unlimited | ~9 quadrillion |
Crypto Support | Native (8-18 decimals) | Limited |
Allocation/Distribution | Advanced with fractional unit separation | Basic |
Exact Division | Guaranteed* | No |
Type Safety | Full TypeScript | Partial |
Immutability | Yes | Yes |
Performance | Excellent | Good |
*For divisors composed of factors 2 and 5 only
FAQs
A precise financial calculation library for JavaScript/TypeScript
We found that @thesis-co/cent demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 4 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Product
Add real-time Socket webhook events to your workflows to automatically receive pull request scan results and security alerts in real time.
Research
The Socket Threat Research Team uncovered malicious NuGet packages typosquatting the popular Nethereum project to steal wallet keys.
Product
A single platform for static analysis, secrets detection, container scanning, and CVE checks—built on trusted open source tools, ready to run out of the box.