Overview
The PPP library offers three rounding strategies to format your final prices. Each strategy serves different use cases, from full precision calculations to marketing-friendly prices.
Rounding Strategies
'none' - Full Precision (Default)
Returns the raw calculated price with full decimal precision. No rounding is applied.
import ppp from '@sachithrrra/ppp';
// Sri Lanka with default rounding
console.log(ppp(10, 'LK'));
// 4.310847260821793
console.log(ppp(10, 'LK', 0.2, 'none'));
// 4.310847260821793 (same as default)
// India
console.log(ppp(50, 'IN', 0.2, 'none'));
// 19.65857159234886
Use case:
- Internal calculations where you need maximum precision
- When you’ll apply your own rounding logic later
- Database storage before display formatting
- API responses where the client handles formatting
Rounds to 2 decimal places using standard rounding rules. This is the typical format for USD currency.
import ppp from '@sachithrrra/ppp';
// Sri Lanka - rounds to 2 decimals
console.log(ppp(10, 'LK', 0.2, 'currency'));
// 4.31
// India
console.log(ppp(50, 'IN', 0.2, 'currency'));
// 19.66
// Brazil
console.log(100, 'BR', 0.2, 'currency');
// 56.93
Use case:
- Standard e-commerce pricing
- Invoices and receipts
- Financial reporting
- When you need precise, professional formatting
Implementation
The currency rounding is implemented at index.js:54-56 using standard JavaScript rounding:
if (rounding === 'currency') {
// Standard concise rounding (2 decimal places)
return Math.round(finalPrice * 100) / 100;
}
'pretty' - Marketing-Friendly Rounding
Rounds to psychologically appealing price points that are commonly used in marketing. The rules vary based on the price range.
import ppp from '@sachithrrra/ppp';
// Prices under $10: Ends in .49 or .99
console.log(ppp(10, 'LK', 0.2, 'pretty'));
// 4.49 (from 4.31)
console.log(ppp(10, 'EG', 0.2, 'pretty'));
// 2.99 (from 3.00)
// Prices $10-$100: Rounded to nearest integer
console.log(ppp(50, 'IN', 0.2, 'pretty'));
// 20 (from 19.66)
console.log(ppp(50, 'BR', 0.2, 'pretty'));
// 28 (from 28.47)
// Prices over $100: Rounded to nearest 5
console.log(ppp(200, 'PL', 0.2, 'pretty'));
// 120 (from 118.35)
console.log(ppp(500, 'GB', 0.2, 'pretty'));
// 485 (from 483.88)
Use case:
- Consumer-facing pricing pages
- Subscription tiers
- Product catalogs
- Marketing campaigns
- When conversion optimization matters more than precision
Pretty Rounding Rules
The 'pretty' strategy applies different rules based on the price range:
Rule 1: Prices < $10
Rounds to either x.49 or x.99 depending on the decimal value.
import ppp from '@sachithrrra/ppp';
// Decimal < 0.5 → Round to x.49
console.log(ppp(10, 'LK', 0.2, 'pretty'));
// 4.49 (from 4.31, decimal is 0.31 < 0.5)
console.log(ppp(15, 'IN', 0.3, 'pretty'));
// 6.49 (from 6.20, decimal is 0.20 < 0.5)
// Decimal >= 0.5 → Round to x.99
console.log(ppp(10, 'EG', 0.2, 'pretty'));
// 2.99 (from 2.99, decimal is 0.99 >= 0.5)
console.log(ppp(20, 'NG', 0.2, 'pretty'));
// 5.99 (from 5.99, decimal is 0.99 >= 0.5)
Implementation (index.js:63-68)
if (finalPrice < 10) {
// e.g. 4.31 -> 4.49, 2.89 -> 2.99
const integerPart = Math.floor(finalPrice);
const decimalPart = finalPrice - integerPart;
if (decimalPart < 0.5) return integerPart + 0.49;
return integerPart + 0.99;
}
Rule 2: Prices 10−100
Rounds to the nearest integer (no decimals).
import ppp from '@sachithrrra/ppp';
// Standard rounding to integers
console.log(ppp(50, 'IN', 0.2, 'pretty'));
// 20 (from 19.66)
console.log(ppp(100, 'BR', 0.2, 'pretty'));
// 57 (from 56.93)
console.log(ppp(75, 'PL', 0.2, 'pretty'));
// 44 (from 44.39)
console.log(ppp(99, 'MX', 0.2, 'pretty'));
// 63 (from 62.69)
Implementation (index.js:69-72)
else if (finalPrice < 100) {
// e.g. 43.2 -> 43, 48.7 -> 49
return Math.round(finalPrice);
}
Rule 3: Prices > $100
Rounds to the nearest 5 (ends in 0 or 5).
import ppp from '@sachithrrra/ppp';
// Round to nearest 5
console.log(ppp(200, 'PL', 0.2, 'pretty'));
// 120 (from 118.35)
console.log(ppp(300, 'IN', 0.2, 'pretty'));
// 115 (from 117.96)
console.log(ppp(500, 'BR', 0.2, 'pretty'));
// 285 (from 284.63)
console.log(ppp(999, 'MX', 0.2, 'pretty'));
// 630 (from 632.94)
Implementation (index.js:73-75)
else {
// e.g. 132 -> 130, 134 -> 135, 138 -> 140
return Math.round(finalPrice / 5) * 5;
}
Comprehensive Comparison
Here’s how all three rounding strategies compare for the same prices:
import ppp from '@sachithrrra/ppp';
function compareRounding(price, countryCode) {
console.log(`\n$${price} in ${countryCode}:`);
console.log(` none: $${ppp(price, countryCode, 0.2, 'none')}`);
console.log(` currency: $${ppp(price, countryCode, 0.2, 'currency')}`);
console.log(` pretty: $${ppp(price, countryCode, 0.2, 'pretty')}`);
}
compareRounding(10, 'LK');
// $10 in LK:
// none: $4.310847260821793
// currency: $4.31
// pretty: $4.49
compareRounding(50, 'IN');
// $50 in IN:
// none: $19.65857159234886
// currency: $19.66
// pretty: $20
compareRounding(200, 'BR');
// $200 in BR:
// none: $113.85261463192578
// currency: $113.85
// pretty: $115
compareRounding(999, 'GB');
// $999 in GB:
// none: $967.8732271551697
// currency: $967.87
// pretty: $970
Real-World Examples
Example 1: SaaS Subscription Tiers
import ppp from '@sachithrrra/ppp';
const plans = {
starter: 9,
professional: 29,
enterprise: 99
};
function getPricingForCountry(countryCode) {
return {
starter: ppp(plans.starter, countryCode, 0.2, 'pretty'),
professional: ppp(plans.professional, countryCode, 0.2, 'pretty'),
enterprise: ppp(plans.enterprise, countryCode, 0.2, 'pretty')
};
}
// India pricing
console.log(getPricingForCountry('IN'));
// { starter: 3.49, professional: 11.99, enterprise: 39 }
// Brazil pricing
console.log(getPricingForCountry('BR'));
// { starter: 4.99, professional: 16.99, enterprise: 56 }
// Poland pricing
console.log(getPricingForCountry('PL'));
// { starter: 4.99, professional: 16.99, enterprise: 59 }
Notice how 'pretty' rounding creates psychologically appealing prices:
- Under 10:.49 or $.99 endings
- 10−100: Clean integers
- The prices look intentional and professional
Example 2: E-commerce Product Pricing
import ppp from '@sachithrrra/ppp';
const products = [
{ name: 'Ebook', price: 14.99 },
{ name: 'Video Course', price: 79 },
{ name: 'Masterclass Bundle', price: 249 }
];
function getProductPricing(product, countryCode) {
return {
name: product.name,
usd: product.price,
local: ppp(product.price, countryCode, 0.2, 'pretty')
};
}
// Sri Lanka pricing
products.forEach(product => {
console.log(getProductPricing(product, 'LK'));
});
// { name: 'Ebook', usd: 14.99, local: 6.49 }
// { name: 'Video Course', usd: 79, local: 34 }
// { name: 'Masterclass Bundle', usd: 249, local: 105 }
Example 3: Currency Rounding for Invoices
import ppp from '@sachithrrra/ppp';
function generateInvoice(items, countryCode) {
const subtotal = items.reduce((sum, item) => {
return sum + ppp(item.price, countryCode, 0.2, 'currency');
}, 0);
const tax = Math.round(subtotal * 0.10 * 100) / 100; // 10% tax
const total = Math.round((subtotal + tax) * 100) / 100;
return { subtotal, tax, total };
}
const cartItems = [
{ name: 'Product A', price: 29.99 },
{ name: 'Product B', price: 49.99 },
{ name: 'Product C', price: 19.99 }
];
// India invoice
console.log(generateInvoice(cartItems, 'IN'));
// { subtotal: 39.32, tax: 3.93, total: 43.25 }
// Uses 'currency' rounding for precise financial calculations
For invoices and receipts, always use 'currency' rounding to ensure accurate financial records and tax calculations.
Choosing the Right Strategy
Use 'none' when:
- You need maximum precision for calculations
- You’re storing prices in a database
- You’ll apply custom formatting later
- You’re building an API that returns raw values
Use 'currency' when:
- You need standard financial formatting
- Generating invoices or receipts
- Professional business contexts
- Accounting and reporting
Use 'pretty' when:
- Building consumer-facing pricing pages
- Optimizing for conversion
- Marketing campaigns
- Subscription plan displays
- Product catalogs
Validation
The library validates the rounding parameter at index.js:38-40:
if (typeof rounding !== 'string' || !['none', 'currency', 'pretty'].includes(rounding)) {
throw new Error("Rounding must be one of: 'none', 'currency', 'pretty'.");
}
Invalid rounding values throw an error:
import ppp from '@sachithrrra/ppp';
// This will throw an error
try {
ppp(10, 'US', 0.2, 'invalid');
} catch (error) {
console.error(error.message);
// "Rounding must be one of: 'none', 'currency', 'pretty'."
}
Always use one of the three valid strategies: 'none', 'currency', or 'pretty'. Any other value will throw an error.