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.
Popular libraries like dinero.js are built on JavaScript's Number type, which has fundamental limitations:
- Precision Loss: JavaScript's
Numbercan only safely represent integers up toNumber.MAX_SAFE_INTEGER(2⁵³ - 1) - Floating Point Errors: Floating point arithmetic can introduce rounding errors
- Limited Scale: Struggles with high-precision assets like cryptocurrencies. Neither assets on Bitcoin (8 decimals), Solana (9 decimals), or Ethereum (18 decimals) fit into a JS
Number.
cent solves these problems with:
- 🔢 Arbitrary Precision: Uses
BigIntfor unlimited precision arithmetic - 💰 Multi-Asset Support: Handles traditional currencies, cryptocurrencies, and custom assets
- 🧮 Exact Mathematics: Fxied point math that guarantees exacy results, with options for non-fixed calculations.
- 🌍 Comprehensive Currency Database: Built-in support for 180+ world currencies
- 🎯 Type Safety: Full TypeScript support with strict type checking
- 🔄 Immutable: All operations return new instances, preventing accidental mutations
- ✨ Ergonomic API: Clean factory functions for creating numbers from strings
import{Money,Price}from'@thesis/cent'// Creationconstusd=Money("$100.50")constbtc=Money("0.5 BTC")// Arithmeticconsttotal=usd.add(Money("$25.25"))// $125.75// Conversion with precision preservationconstprice=newPrice(Money("$50,000"),Money("1 BTC"))constconverted=usd.convert(price)// Exact BTC amount// Allocation and distributionconst[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]// Formattingusd.toString({locale: "en-US",compact: true})// "$100.50"btc.toString({preferredUnit: "satoshi"})// "50,000,000 sat"The Money() factory function makes working with currencies simple:
import{Money}from'@thesis/cent'// Parse currency symbols with auto-detectionconstusd=Money('$1,234.56')// US Dollar: $1,234.56consteur=Money('€1.234,56')// Euro (EU format): €1,234.56constgbp=Money('£999.99')// British Pound: £999.99constjpy=Money('¥50,000')// Japanese Yen: ¥50,000// Parse currency codes (case insensitive)constdollars=Money('USD 100.50')consteuros=Money('100.50 EUR')// Parse cryptocurrency main unitsconstbitcoin=Money('₿2.5')// Bitcoin: 2.5 BTCconstethereum=Money('ETH 10.123456')// Ethereum: 10.123456 ETH// Parse cryptocurrency sub-unitsconstsatoshis=Money('1000 sat')// 1000 satoshis = 0.00001000 BTCconstwei=Money('1000000 wei')// 1000000 wei = 0.000000000001 ETHconstgwei=Money('50 gwei')// 50 gwei = 0.00000005 ETH// Parse with fractional unit symbolsconstsats=Money('§10000')// 10000 satoshis = 0.0001 BTCconstcents=Money('¢50')// 50 cents = $0.50constpence=Money('p75')// 75 pence = £0.75// Supports negative amountsconstdebt=Money('-$500.25')constrefund=Money('€-123.45')// Financial precision - allows sub-cent amountsconstprecise=Money('$100.12345')// 5 decimal places preservedconstmicroYen=Money('¥1000.001')// Sub-yen precisionA 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 instancesconsteuros=newMoney({asset: EUR,amount: {amount: 50025n,decimals: 2n}// €500.25})constdollars=newMoney({asset: USD,amount: {amount: 100000n,decimals: 2n}// $1,000.00})// Basic arithmetic (same currency only)constsum=euros.add("€250.50")console.log(sum.toString())// "€750.75"// Multiplication and divisionconstdoubled=euros.multiply("2")consthalf=euros.divide("2")// Only works with factors of 2 and 5// Comparisonsconsole.log(euros.greaterThan(dollars))// Error: Different currenciesconsole.log(euros.isPositive())// trueconsole.log(euros.equals(euros))// true// Sorting arrays using compare methodconstamounts=[Money("$100"),Money("$50"),Money("$200")]constsorted=amounts.sort((a,b)=>a.compare(b))console.log(sorted.map(m=>m.toString()))// ["$50.00", "$100.00", "$200.00"]// Formatting optionsconsole.log(euros.toString({locale: 'de-DE'}))// "500,25 €"console.log(euros.toString({compact: true}))// "€500"// Fractional unit symbol formattingconstbtc=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 distributionconstbudget=Money("$1000")// Allocate proportionally by ratiosconst[marketing,development,operations]=budget.allocate([2,5,3])// Results: [$200, $500, $300] (2:5:3 ratio)// Distribute evenlyconst[alice,bob,charlie]=budget.distribute(3)// Results: [$333.34, $333.33, $333.33] (remainder to first)// Handle fractional units separatelyconstprecise=Money("$100.00015")constparts=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 numbersconstprice=FixedPoint('1255.50')// Auto-detects 2 decimalsconstrate=FixedPoint('0.875')// Auto-detects 3 decimals// Percentage strings are automatically converted to decimalsconstpercentage=FixedPoint('51.5%')// Becomes 0.515 (auto-detects 3 decimals)consttax=FixedPoint('8.25%')// Becomes 0.0825 (auto-detects 4 decimals)// Arithmetic operations with automatic precision handlingconstproduct=price.multiply("0.875")console.log(product.toString())// "1098.5625"// Use percentage parsing in calculationsconsttotalWithTax=price.multiply(FixedPoint('8.25%'))console.log(totalWithTax.toString())// "103.5788" (8.25% of $1255.50)// Precise division (only multiples of 2 and 5)consthalf=price.divide("2")constfifth=price.divide("5")consttenth=price.divide("10")// Comparison operationsconsole.log(price.greaterThan("0.875"))// trueconsole.log(price.lessThanOrEqual("0.875"))// false// Also supports original constructor for explicit controlconstexplicit=newFixedPointNumber(125550n,2n)// Same as FixedPoint('1255.50')// Rational - fractions and exact arithmetic// Create from fraction stringsconstoneThird=Rational('1/3')consttwoFifths=Rational('2/5')// Create from decimal strings (auto-converted to fractions)constquarter=Rational('0.25')// Becomes 1/4constdecimal=Rational('0.125')// Becomes 1/8// Create directly from bigint numerator and denominatorconstpi=Rational(22n,7n)// 22/7 approximation of πconstoneThird=Rational(1n,3n)// 1/3// Exact arithmeticconstsum=oneThird.add("2/5")// (1/3) + (2/5) = 11/15console.log(sum.toString())// "11/15"constproduct=oneThird.multiply("2/5")// (1/3) * (2/5) = 2/15console.log(product.toString())// "2/15"// Automatic simplificationconstsimplified=Rational('6/9')console.log(simplified.toString())// "2/3"// Also supports original constructorconstexplicit=newRationalNumber({p: 1n,q: 10n})// Same as Rational('1/10')// Seamless conversion between typesconstrational=Rational('3/8')constdecimalStr=rational.toDecimalString()// "0.375"constfixedPoint=FixedPoint(decimalStr)// Auto-detects 3 decimalsconsole.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-timestampingconstusdEur=newExchangeRate(USD,EUR,"1.08")// 1.08 EUR per USDconsole.log(usdEur.toString())// "1.08 €/$"// 2. Individual arguments with custom timestamp and sourceconstbtcUsd=newExchangeRate(BTC,USD,"50000","1640995200",// 2022-01-01 timestamp{name: "Coinbase",priority: 1,reliability: 0.95})consteurJpy=newExchangeRate({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 quoteconsteurUsd=usdEur.invert()// 1 EUR = 0.925925... USDconsole.log(eurUsd.toString())// "0.925925925925925926 $/@"// Cross-currency calculations via multiplication// EUR/USD × USD/JPY = EUR/JPY (USD cancels out)consteurUsdRate=newExchangeRate(EUR,USD,"1.0842")constusdJpyRate=newExchangeRate(USD,JPY,"149.85")consteurJpyCalculated=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 ratesconstdollars=Money("$100.00")consteuros=usdEur.convert(dollars)console.log(euros.toString())// "€108.00"// Reverse conversion (automatic direction detection)constbackConverted=usdEur.convert(euros)console.log(backConverted.toString())// "$100.00"// Automatic direction detection - same rate works both waysconstrate=newExchangeRate(USD,EUR,"1.08")// 1 USD = 1.08 EURconstusd100=Money("$100")consteur108=rate.convert(usd100)// USD → EUR: $100 → €108console.log(eur108.toString())// "€108.00"constconvertBack=rate.convert(eur108)// EUR → USD: €108 → $100console.log(convertBack.toString())// "$100.00"// Works with any amount and either currency in the rateconstmoreEuros=Money("€540")// €540constconvertedDollars=rate.convert(moreEuros)// €540 ÷ 1.08 = $500console.log(convertedDollars.toString())// "$500.00"// Exchange rate averaging for multiple sourcesconstrate1=newExchangeRate(USD,EUR,"1.07")constrate2=newExchangeRate(USD,EUR,"1.09")constaveraged=newExchangeRate(ExchangeRate.average([rate1,rate2]))console.log(averaged.rate.toString())// "1.080" (average of 1.07 and 1.09)// Time-based operationsconstcurrentRate=newExchangeRate(USD,EUR,"1.08")console.log(currentRate.isStale(300000))// false (less than 5 minutes old)// Formatting optionsconsole.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 supportconstserialized=usdEur.toJSON()constrestored=ExchangeRate.fromJSON(serialized)console.log(restored.equals(usdEur))// true// create bid/ask spreads for tradingconstrate=newExchangeRate(USD,EUR,"1.2000")// apply spread using decimal string (2% spread)const{ bid, ask, mid }=rate.spread("0.02")// "2%" also worksconsole.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)constAPPLE={name: 'Apple',code: 'APPLE',decimals: 0n,symbol: '🍎'}constORANGE={name: 'Orange',code: 'ORANGE',decimals: 0n,symbol: '🍊'}// Create price ratiosconstusdPerApple=newPrice(Money("$5.00"),// $5.00{asset: APPLE,amount: {amount: 1n,decimals: 0n}}// 1 apple (custom asset))constapplesPerBtc=newPrice({asset: APPLE,amount: {amount: 10000n,decimals: 0n}},// 10,000 applesMoney("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 😉constusdPerBtc=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/BTCconstcalculatedApplesPerBtc=usdPerBtc.divide(usdPerApple)// Scalar operations (multiply/divide by numbers)constdoubledPrice=usdPerApple.multiply("2")// $10.00/appleconsthalfPrice=usdPerApple.divide("2")// $2.50/apple// Convert to mathematical ratioconstratio=usdPerApple.asRatio()// RationalNumber: 500/1// Price operations validate shared assetstry{constorangesPerBtc=newPrice({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 stringsconstrange1=PriceRange("$50 - $100")constrange2=PriceRange("$50-100")// Compact formatconstrange3=PriceRange("€25 - €75")// Create from Money instancesconstrange4=PriceRange(Money("$50"),Money("$100"))// Mixed creationconstrange5=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 queriesconsole.log(range1.contains(Money("$75")))// trueconsole.log(range1.contains("$25"))// falseconsole.log(range1.isAbove(Money("$40")))// true (entire range above $40)console.log(range1.isBelow(Money("$120")))// true (entire range below $120)// Range mathematicsconstrange6=PriceRange("$80 - $150")console.log(range1.overlaps(range6))// trueconstintersection=range1.intersect(range6)console.log(intersection?.toString())// "$80.00 - $100.00"constunion=range1.union(range6)console.log(union.toString())// "$50.00 - $150.00"// Split ranges into equal partsconstparts=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 patternsconstunderRange=PriceRange.under(Money("$100"))// "$0.00 - $100.00"constoverRange=PriceRange.over(Money("$50"),Money("$500"))// "$50.00 - $500.00"constaroundRange=PriceRange.around(Money("$100"),"10%")// "$90.00 - $110.00"// Create price buckets for filtersconstbuckets=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 optionsconsole.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 formattingconsteurRange=PriceRange("€50 - €100")console.log(eurRange.toString({locale: "de-DE"}))// "50,00 € - 100,00 €"// Large ranges with compact notationconstlargeRange=PriceRange("$1000000 - $5000000")console.log(largeRange.toString({compact: true}))// "$1M - $5M"// Currency conversionconstexchangeRate=newExchangeRate(USD,EUR,"0.85")constconvertedRange=range1.convert(exchangeRate)console.log(convertedRange.toString())// "€42.50 - €85.00"// E-commerce product filteringconstproducts=[{name: "Budget Widget",price: Money("$45")},{name: "Standard Widget",price: Money("$75")},{name: "Premium Widget",price: Money("$125")},{name: "Deluxe Widget",price: Money("$95")}]constpriceFilter=PriceRange("$50 - $100")constaffordableProducts=products.filter(product=>priceFilter.contains(product.price))console.log(affordableProducts.map(p=>p.name))// ["Standard Widget", "Deluxe Widget"]// JSON serialization for APIs and storageconstserialized=range1.toJSON()console.log(JSON.stringify(serialized))constrestored=PriceRange.fromJSON(serialized)console.log(restored.equals(range1))// true// Cryptocurrency ranges with full precisionconstbtcRange=PriceRange("₿0.001 - ₿0.01")console.log(btcRange.contains(Money("₿0.005")))// trueconsole.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 currenciesconsole.log(USD.decimals)// 2nconsole.log(USD.symbol)// "$"console.log(USD.fractionalUnit)// "cent"// Cryptocurrencies with high precisionconsole.log(BTC.decimals)// 8nconsole.log(BTC.fractionalUnit)// Complex object with multiple units// Currencies with no decimalsconsole.log(JPY.decimals)// 0nSafe serialization for APIs and storage:
constmoney=Money("$1,234,567,890,123.45")// serialize (BigInt becomes string)constjson=money.toJSON()console.log(JSON.stringify(json))// {"asset":{"name":"United States dollar","code":"USD","decimals":"2","symbol":"$"},"amount":"1234567890123.45"}// Deserializeconstrestored=Money.fromJSON(json)console.log(restored.equals(money))// true// FixedPointNumber also serializes as decimal strings preserving trailing zerosconstfp=FixedPoint("12.34500")console.log(JSON.stringify(fp))// "12.34500"constrestoredFp=FixedPointNumber.fromJSON("12.34500")console.log(restoredFp.equals(fp))// truecent automatically handles different precisions:
// different decimal places are automatically normalizedconstfp1=FixedPoint("10.0")// 1 decimalconstfp2=FixedPoint("5.00")// 2 decimalsconstsum=fp1.add("5.00")// Normalized to 2 decimalsconsole.log(sum.toString())// "15.00"Unlike floating-point arithmetic, cent ensures exact division results:
constnumber=FixedPoint("100")// 100console.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 precisionconstwireTransfer=Money("$9,999,999,999.99")constfee=wireTransfer.multiply("0.005")// 0.5% feeconstafterFee=wireTransfer.subtract(fee)// handle Bitcoin with satoshi and sub-satoshi precisionconstsatoshiAmount=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)constweiAmount=Money("1 ETH")// Also supports original constructor for explicit controlconstexplicit=newMoney({asset: ETH,amount: {amount: 1000000000000000000n,decimals: 18n}// Same as Money("1 ETH")})weiAmount.equals(Money("Ξ1.0"))// true// Allocate amounts without losing precisionconstrevenue=Money("$12,345.67")// Proportional allocation by department budgetsconst[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 membersconstbonus=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 accountingconstpreciseAmount=Money("$1,000.00123")// High-precision amountconstparts=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-unitsconst[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- Currency symbols:
Money('$100.50'),Money('€1.234,56'),Money('£999') - Currency codes:
Money('USD 100'),Money('100.50 EUR')(case insensitive) - Crypto main units:
Money('₿2.5'),Money('ETH 10.123456') - Crypto sub-units:
Money('1000 sat'),Money('50 gwei'),Money('1000000 wei') - Negative amounts:
Money('-$500'),Money('$-123.45') - Sub-unit precision:
Money('$100.12345')(preserves exact precision)
- Currency symbols:
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'))
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 change
Comparison 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 value
State 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 precision
Conversion & 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 JSON
Arithmetic Operations (accepts FixedPoint objects or string arguments):
add(other)- Additionsubtract(other)- Subtractionmultiply(other)- Multiplicationdivide(other)- Safe divisionnormalize(target)- Change decimal precision
Comparison 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 value
Utility Methods:
toString()- DecimalString representationparseString(str, decimals)- Parse from string with explicit decimalsfromDecimalString(str)- Parse from DecimalString with auto-detected decimals
Arithmetic Operations (accepts Ratio objects, fraction strings, or decimal strings):
add(other)- Exact additionsubtract(other)- Exact subtractionmultiply(other)- Exact multiplicationdivide(other)- Exact division
Comparison 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 value
Utility 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)
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 selection
Constructor Overloads:
new ExchangeRate(data)- Create from ExchangeRateData objectnew ExchangeRate(baseCurrency, quoteCurrency, rate, timestamp?, source?)- Create from individual arguments
Exchange 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 precision
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 range
Range 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 parts
Conversion & 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 equality
Static 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 buckets
| Feature | 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