Home > OS >  Node.js and rounding, should it be this hard...?
Node.js and rounding, should it be this hard...?

Time:11-19

I know this has been asked many times before, and we've settled for a solution on rounding amounts (currency) with two decimals as:

const myCurrencyAmount = Number(Math.round(totalAmount   'e'   2)   'e-'   2)

However, it is now very, very close to 2022 and Elon Musk is about to send people to Mars (well, the moon first, but...) so surely there must be a better way to round (safely!) in Node.js by now...???

The above solution has been working fine now for some time (years) but as soon as you want to start adding or do calculations using two decimals (like in invoices for example) it quickly becomes arduous to keep track of rounding all number, all the time!

Because in Node.js, of course, if you round a number once, to two decimals, it's not going to remember that and use its floating point again the next time you want to read the variable messing up your calculations anew...

And for you, who thinks but why not simply use .toFixed(2), I can tell you that the world of floats will bring you some hurt sooner or later... or, in the meantime just enjoy these lovely samples:

const num = 35.855;
console.log(num.toFixed(2));
// LOGS: 35.85
const num2 = 1.005;
console.log(num2.toFixed(2));
// LOGS: 1.00

Then imagine summarizing a few thousand invoice lines with many thousands in quantity and see how much those rounding errors throws you off...

Please note that I am not asking about floating points (as I do understand them) I am simply asking for a solution with less characters to write...

CodePudding user response:

I'd suggest looking at a dedicated library for this purpose, for example currency.js. This handles most of the issues you are concerned with, it is designed for this use case.

For example:

console.log('7.66   0.01:',currency(7.66).add(.01).value); 

console.log('$1.52 - $0.02:', currency('$1.52').subtract('$0.02').format());

let c1 = currency(2.03);
let c2 = currency(3.29);
console.log('11.66   2.03   3.29:', currency(11.66).add(c1).add(c2).value);

// With GBP
console.log('\nGBP example');
const invoiceAmount =  currency(113535, { symbol: "£" });
const discountRate = 0.05;
const vatRate = 0.125;
const discount = invoiceAmount.multiply(discountRate);
const invoiceIncDiscount = invoiceAmount.subtract(discount);
const vat = invoiceIncDiscount .multiply(vatRate);
const invoiceTotal = invoiceIncDiscount .add(vat);
console.log('Invoice amount:'.padEnd(30), invoiceAmount.format().padStart(20));
console.log(`Discount (${discountRate*100}%):`.padEnd(30), discount.format().padStart(20));
console.log('Invoice (with discount):'.padEnd(30), invoiceIncDiscount .format().padStart(20));
console.log(`VAT (${vatRate*100}%):`.padEnd(30), vat.format().padStart(20));
console.log('Invoice (inc. VAT):'.padEnd(30), invoiceTotal.format().padStart(20));


// From the docs (European number format   '€' symbol 
 console.log('\nEuro example');
const euro = value => currency(value, { symbol: "€", separator: ".", decimal: "," });
console.log('€2.573.693,75   €100.275,50 =', euro("2.573.693,75").add("100.275,50").format());
console.log('€1.237,72 - €300 =', euro("1.237,72").subtract(300).format());
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://unpkg.com/currency.js/dist/currency.min.js"></script>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

If you're looking for a more general purpose rounding function I'd have a look at lodash round, this will round a number to any arbitrary precision.

    
const num = 35.855;
console.log(_.round(num, 2));

const num2 = 1.005;
console.log(_.round(num2, 2));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

  • Related