I am trying to improve my simple invoice system made with TiddlyWiki and came across this mathematical oddity and am trying to determine if this is just one of those “javascript math precision” oddities or a bug in the filter code. These are two examples of a filtered transclusion to get a bunch of billable entries associated with an invoice number, multiply some billable hours by some rate, then add them all up and calculate the tax using a <<tax_rate>> variable:
Moving fixed to the end fixes the non-rounding but this one sometimes (for a tax_rate=0.1, for instance) gives a number off by a cent due to accumulated numeric cruft after the second decimal place that then gets rounded.
It shouldn’t matter if I multiply each one by the tax rate and then add them up or add them up and then multiply the subtotal by the tax rate. However, This last one gives what I would expect the answer to be.
I’ve kind of solved my problem by using the last example but I was just throwing it out there for folks more intimately knowledgeable about how javascript handles precision to see if this was expected behaviour or something faulty going on inside the reduce run.
Actually … those should be add<accumulator> but because I didn’t put quotes around it, the <accumulator> part got eaten by the forum parser. Fixed now.
Hi @mwiktowy I think you’re running into the peculiarities of floating point notation, which is the format that TiddlyWiki and JavaScript use to represent numbers.
The issue is that neat decimals don’t necessarily correspond to a neat representation in binary. This can lead to odd results – for example try 1.1*100 in a JavaScript console and you’ll get 110.00000000000001.
You can avoid most of the rounding problems, if you start your numbers with * 1000. Make your calculations, divide the result by 1000 and cut the result down to 2 decimals.
Thanks @jeremyruston . I have read that (or similar) articles but was surprised that is showed up when I didn’t have that many significant digits. I’ll double check the dynamic range of my input numbers … maybe some garbage is not being ignored.
It is possible that the last solution that worked will fall down in other corner-cases so I will implement the conversion to dealing in integer amount of milli-dollars and then truncate to cents like @pmario helpfully suggested.
IMO the best way to go is to stay with the full range, big numbers until you have the sum and then make divisions and percentage calculations and as the last step truncate. ..
The problem here is that it really is truncating and not rounding.