Financial calculations

This guidance applies to LiteFarm’s financial calculations. Specifically, the guidance applies to currency amounts (money) and to time periods used to compute pay and other financial figures.

In general, financial computations should not involve toFixed(); use the approach described below instead. The toFixed() function is suitable (if not necessary) for formatting the display of numbers, including financial results.

In financial calculations, numbers should always be rounded to the nearest hundredth (two decimal places), using the roundToTwoDecimals() function exported from packages/webapp/src/util/index.js:

export const roundToTwoDecimal = (value) => { return Math.round(value * 100) / 100; };

Applying this function to inputs (terms) or to final results is not sufficient. In general, when there is an expression a + b where a and b are financial numbers as described above, the rounding function should be applied to all terms (both a and b) and also to the result:

roundToTwoDecimal(roundToTwoDecimal(a) + roundToTwoDecimal(b))

If this seems excessive, consider adding 0.0401 and 0.13999999999999999 (Note: examples here use addition, but the guidance applies to subtraction, multiplication, and division as well.)

A browser console shows us that JavaScript’s Number type has the usual floating-point pitfalls. This is not the result we get from a calculator or hand calculation.

> 0.0401 + 0.13999999999999999 < 0.18009999999999998

That’s alright because we want some rounding anyway. We want the first number to round to 0.04 and the second to round to 0.14 for a sum of 0.18

Our function rounds the terms correctly:

> roundToTwoDecimal(0.0401) < 0.04 > roundToTwoDecimal(0.13999999999999999) < 0.14

But the floating-point weirdness seeps back in when we add. This is not what we want.

We must apply the rounding function to all terms and the result to guarantee the proper outcome.

For most sets of numbers, removing some of the rounding calls will still give a correct outcome. But for some sets— like the one shown— we need all the calls. Since we don’t know what the numbers will be, we need all the calls to be sure we’re correct.

There are some cases where we can avoid calling the rounding function. This code is correct.

In the final assignment, the rounding function is not called for the variables rate and hoursWorked simply because they were obviously rounded by the preceding lines. Notice that there is no call for the constant term 60 either, because we know it’s an integer without any problematic fractional part.

In general, the rounding function should be applied to all non-integer values used in financial calculations, even if they already “have two decimal places”. Only the last expression below gives the result we want.

In financial calculations, use += (and similar operators) with great care if at all, because the result will not be rounded. The loop example above would be incorrect if the last assignment used +=. The browser console illustrates this.