Live data from Hacker News

How to Handle Monetary Values in JavaScript

frontstuff.io

101–110 of 115 posts

Re: How to Handle Monetary Values in JavaScript

#101
post #24

> Floats: 0.1 + 0.2 // returns 0.30000000000000004 The question is how much of an issue is this really? How often is 4 parts in 10 quadrillion a meaningful error when dealing with money? Especially when most of the time JS is dealing with money it will be presentation.

After doing a lot of calculations? This is a big problem.

The firm I worked at used built in Java libraries and always deferred rounding until the very last possible moment.

Re: How to Handle Monetary Values in JavaScript

#102
post #32
post #24

> Floats: 0.1 + 0.2 // returns 0.30000000000000004 The question is how much of an issue is this really? How often is 4 parts in 10 quadrillion a meaningful error when dealing with money? Especially when most of the time JS is dealing with money it will be presentation.

Yeah seriously. You'll have some front-end lib func converting that to 2 significant digits right of the decimal anyway. Weak argument imo haha.

[deleted]

Re: How to Handle Monetary Values in JavaScript

#103

Earlier quoted context omitted.

You need a timestamp and a timeseries database of exchange rates so that you can go back in time and reconstruct your calculations at a latter date as well. Source: worked at a bank that implemented pretty much this.

To multiple different currencies at multiple valuation points. With optional exchange rate costs. It's a pain in the arse ... Source: worked on multi-currency fund tracking software.

Agreed with both of you, and using an exchange rate at a specific point in time actually could make deferring that calculation even more of a sound idea, depending on the exact semantics of what you’re computing.

At larger scales even exchange rates are a leaky abstraction over a forex market, so there you go. No solution will work everywhere.

But if you’re entering the prices of last week’s coffee and hotel room into a web app to see how much you’ll be reimbursed, you don’t want to use a new exchange rate every time a form input changes.

Source: worked on multi-currency expense reporting software where everything is done in JavaScript floats.

Re: How to Handle Monetary Values in JavaScript

#104
post #31

Banking apps, e-commerce websites, stock exchange platforms, we interact with money daily. We also increasingly rely on technology to handle ours. Yet, there’s no consensus around how to programmatically handle monetary values. I wonder if the average person would find this frightening.

IBM has had a solution for a long time: http://speleotrove.com/decimal/decarith.html Python uses it: https://docs.python.org/3.5/library/decimal.html Just because JS is stupid doesn't mean other languages are.

> Just because JS is stupid doesn't mean other languages are.

This bewilders me. They keep cramming so much new stuff into JS these days - arrow operators, promises, async/await, God knows what, but not ints? I'm not asking for the whole numeric tower of Lisp languages, I can live without rationals and bignums and complex numbers. But a God's own fixed-width integer. You'd think they'd add it in the years since JS started being used for serious things.

Re: How to Handle Monetary Values in JavaScript

#105
post #89

Earlier quoted context omitted.

> I thought it was fairly obvious I wasn't talking about banking Right, but the article is talking about all use cases of currency... not all of which have customers that care about penny precision. I think we're in agreement that the business use case should be driving the technical requirements, and sometimes software engineers overestimate the precision actually necessary. Overflow modes aren't always consistent b…

> not all of which have customers that care about penny precision. No, I think all customers care about penny precision when you are talking about money they have or owe. Either the amounts in question are small enough that a penny isn't negligible, even if it's not important, or the amounts are large enough that an error like that really shouldn't exist, because they should be taking it very seriously. People might…

> No, I think all customers care about penny precision when you are talking about money they have or owe.

That wasn't my personal experience working on a fixed income desk. We regularly interacted with CFOs and corporate treasurers and the 'bills' were in the millions. And why would they? As they say, 'penny wise, pound foolish'.

> No, you're generally not throwing out precision when you round in this case. You're throwing out error.

That's not always true from a numerical analysis standpoint. By the way, I highly recommend this old but still useful doc which goes through the math carefully:

https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.h...

Rounding can introduce up to 0.5 ulp (unit in the last place) of error, where ulp here is the precision that you round to. This is pretty easy to show:

  let intSum = 0;
  let floatSum = 0;
  let roundedFloatSum = 0;
  for (let i = 0; i 
Here we're generating some 3-fig decimal numbers and summing them up, once in exact precision, once with floats, and once with intermediate rounding to the 2nd decimal place.

On the last run, this outputs:

  5567.347 5567.347000000001 5567.39
  9.094947017729282e-13 0.0430000000005748
'Rounding as you go' for your intermediate results here introduced an unnecessary 0.043 of absolute error.

Every guide to numerical analysis I've ever seen recommends keeping all calculations in the same format (whether fixnums or floats) all the way through, then only doing rounding at the very end, for this very reason.

In fact, we can prove that on reasonable accounting inputs and simple calculations, doing everything using doubles and then rounding at the very end gives you the _exact_ result.

Let's assume the numbers you're summing, multiplying, etc. remain bounded under 1. Doubles have 52-bits, or about 15 decimal digits of precision

2. Basic arithmetic operations (addition, subtraction, multiplication, division) introduce at most 0.5 ulp of error per operation. Using our assumptions, each decimal number is accurate to at least the billionth (9th digit) place, whereas we only need accuracy to the hundredths.

3. The cumulative error of a chain of a million operations, each with an error in the 9th digit place, can at most only affect the 3rd decimal digit. The 2nd decimal will always be correct

> No, you're generally not throwing out precision when you round in this case. You're throwing out error.

Floating point calculations round to the available precision after every operation. If introducing extraneous rounding to a much lower precision magically 'fixed' errors, then how could long floating point calculations themselves be inaccurate? In fact, there are algorithms that lower overall error by carefully shepherding the low-order digits, e.g. https://en.wikipedia.org/wiki/Kahan_summation_algorithm

Rounding is often appropriate if the rounded value is the actual source of truth, e.g. if, after some long sequence of calculations, you've told the customer that they have $10.15 in the bank, then you should try to store that value rather than the raw float result. Even that's pretty subtle though -- e.g. if their account balance is a result of interest payments, one can show that you will introduce more error in the total interest paid over time if you discard lower order digits rather than reusing them for the next interest calculation.

One last thing: When you hand your exact precision results to an accountant, auditor, or customer, Excel's a pretty common tool that they use for basically everything right? It must be sporting some fancy arbitrary precision or decimal machinery under the hood, right?

Nope, just floats all the way down! https://en.wikipedia.org/wiki/Numeric_precision_in_Microsoft...

I think you should be strongly questioning your assumption that floats aren't 'good enough' if the very first thing every customer you interact with does is cast your results into a floats to do their own calcs.

Re: How to Handle Monetary Values in JavaScript

#106

Maybe it’s just that I’ve done a few gigs at financial institutions, but it’s pretty shocking to see the number of people in this thread arguing that it’s a-ok to use floating point for money. I don’t want to get dragged down into specifics, but if you’re doing this, please, please don’t. Money is one of those things like dates and times - everyone who hasn’t done much of it can’t figure out what all the fuss is abou…

I don't know why people wouldn't just make and array for monetary values or make two int values for decimal and number place.

Re: How to Handle Monetary Values in JavaScript

#107
I usually use cents as the stored amount, so no decimals. Is there something obviously wrong with this approach?

Ie. if the price is 150.95 I store 15095 in the db and do the calculations with cents, and then divide by 100 when presenting the values.

I have never come across fractional cents, maybe that is an issue in some scenarios (would be interested to know what those are... Even tax percentage calculations are rounded to the cent)

Re: How to Handle Monetary Values in JavaScript

#108
post #89

Earlier quoted context omitted.

> not all of which have customers that care about penny precision. No, I think all customers care about penny precision when you are talking about money they have or owe. Either the amounts in question are small enough that a penny isn't negligible, even if it's not important, or the amounts are large enough that an error like that really shouldn't exist, because they should be taking it very seriously. People might…

> No, I think all customers care about penny precision when you are talking about money they have or owe. That wasn't my personal experience working on a fixed income desk. We regularly interacted with CFOs and corporate treasurers and the 'bills' were in the millions. And why would they? As they say, 'penny wise, pound foolish'. > No, you're generally not throwing out precision when you round in this case. You're th…

> > No, you're generally not throwing out precision when you round in this case. You're throwing out error.

> That's not always true from a numerical analysis standpoint.

This isn't about numerical analysis, which is what you seem to not be getting. It's about the medium have specific attributes, and floats being incapable of perfectly representing the value. With respect to the currency being tracked, any difference smaller than one hundredth of a full unit (depending on currency) that results from simple addition or subtraction of accurate values is an error because it's not possible in reality.

> 3. The cumulative error of a chain of a million operations, each with an error in the 9th digit place, can at most only affect the 3rd decimal digit. The 2nd decimal will always be correct

Cumulative error is irrelevant. It only takes a single error that causes a value to be less than the correct value by a very small amount and then if there's any place where it exits the system without rounding, that error may be increased to a full minimum difference of the medium ($0.01 in this case).

> If introducing extraneous rounding to a much lower precision magically 'fixed' errors, then how could long floating point calculations themselves be inaccurate?

By nature of the actual thing being represented by a the floating point value. There is no point talking about floating point without talking about what it's representing. In this case, it's currency, which has very specific characteristics.

You can argue that a sphere is the best container shape because it maximizes volume to surface area all you like, that doesn't mean it's the best container shape when what you are storing is shoe boxes.

> Rounding is often appropriate if the rounded value is the actual source of truth

Yes, nobody is arguing that you shouldn't round floats in this case. I'm arguing you shouldn't use floats so you don't have to round at all.

> Excel's a pretty common tool that they use for basically everything right? It must be sporting some fancy arbitrary precision or decimal machinery under the hood, right? ... Nope, just floats all the way down!

Excel is a system to represent values of all types. Their requirements necessitate an amount of flexibility that makes Floats a good choice. Even then, they will be handling all the rounding and representation automatically for you. When you use excel, you aren't using floats, you're using an excel numeric type which is implemented underneath using floats and some very specific behavior. The fact that it automatically deals with floating point errors and rounding is what makes it not a float.

In the case we are discussing, the same requirements are not present. We don't have to worry about representing any conceivable value, just the ones allowed by the type. A float provides more flexibility than we need, and at the cost of error that needs to be cleaned up at all the edges of the system.

Again, I ask you, why should we choose an underlying type that doesn't fit the needs as well as another option? In all aspects, integer containers either have a less problematic error case or do not suffer the same problem.

Re: How to Handle Monetary Values in JavaScript

#109

Earlier quoted context omitted.

> No, I think all customers care about penny precision when you are talking about money they have or owe. That wasn't my personal experience working on a fixed income desk. We regularly interacted with CFOs and corporate treasurers and the 'bills' were in the millions. And why would they? As they say, 'penny wise, pound foolish'. > No, you're generally not throwing out precision when you round in this case. You're th…

> > No, you're generally not throwing out precision when you round in this case. You're throwing out error. > That's not always true from a numerical analysis standpoint. This isn't about numerical analysis, which is what you seem to not be getting. It's about the medium have specific attributes, and floats being incapable of perfectly representing the value. With respect to the currency being tracked, any difference…

> This isn't about numerical analysis, which is what you seem to not be getting.

Of course it is. The point of the computation is to get the correct result. Numerical analysis hints at whether your computation will give you the right answer or not.

You're asserting that it's trivial to make sure that your fixnums won't overflow, but in most normal cases where you'll fit into a 64-bit int range, you'll also fit into the 52-bit precision range in a double. If you can prove that ints are OK, then you can just as easily prove that doubles are safe to use.

And unlike floating point rounding errors, where the answers often have some hint about the correct result, undetected overflows silently give you a totally garbage result.

Your system does not magically become 'safe by design' simply because you use ints everywhere, you still need to put in the extra work to make sure your numeric range is always sufficient—at which point you're doing numerical analysis, whether you choose to call it that or not.

Honestly, I'm surprised that you will denounce the usage of floating point numbers, repeatedly make untrue claims about FP calculations, then wave off the entire subbranch of CS that studies how numbers are represented on computers and how those calculations can go wrong as being entirely irrelevant.

> It only takes a single error that causes a value to be less than the correct value by a very small amount and then if there's any place where it exits the system without rounding, that error may be increased to a full minimum difference of the medium ($0.01 in this case).

What? The only case where a number rounds wrongly is when the final value is more than half a cent away from the correct value.

If you can't be bothered to understand how FP calculation works, at least please stop making demonstrably false claims and misleading others.

> By nature of the actual thing being represented by a the floating point value.

As soon as you actually do any 'complicated' math (take a square root or a log), your result is no longer exactly representable, because your results aren't decimals or even rationals, but irrationals.

Even an arbitrary precision number type doesn't help you here—your only solution is an arbitrary precision math package—but that isn't what you're suggesting

It sounds like you think using fixnums everywhere guarantees you the Platonic exact answer, but they can't actually do that:

- If you're doing anything complicated, you will unavoidably be making approximations and rounding. Adding a bunch of extra casts back to fixnums don't allow you to avoid approximation, just a false sense of security.

- OTOH, if you're not doing anything complicated, just totaling small numbers multiplied by round constants, then doubles are provably sufficient at getting an exact answer after rounding.

> When you use excel, you aren't using floats, you're using an excel numeric type which is implemented underneath using floats and some very specific behavior. The fact that it automatically deals with floating point errors and rounding is what makes it not a float.

You're contradicting yourself here. All Excel does is 1) compute using floats (doubles) everywhere and 2) round the result before showing it.

That's exactly what I'm advocating. Whether you choose to wrap it into a special Numeric or Money class doesn't change your answer.

Look, here's C#'s decimal class: https://docs.microsoft.com/en-us/dotnet/csharp/language-refe...

Must use some fancy arbitrary precision arithmetic right? No, it's just a floating point number—just a particularly wide (128-bit) type.

Re: How to Handle Monetary Values in JavaScript

#110

Earlier quoted context omitted.

I think that's the disconnect here — 'high' finance is not an exact science, and clients don't actually have an easy way of checking the numbers directly themselves — not only the models, but the inputs are also proprietary. Even auditors and regulators are dependent on bank models. Note that the article's advice is to use their own library, but the library just uses JS floats as ints and a separate precision arg: ht…

> This doesn't give you any more precision than using floats directly, just a wider 'mantissa' range. I think this analysis misses a deep and important point. The library you linked uses decimal floating point (in software) instead of the binary floating point used by IEEE floats. The point of this is not to increase overall precision. A double is already precise to 2e-14% of its value, which means it's capable of re…

> This allows us to have no error

That's not true, because the software 'mantissa' in that library still only has 52 bits of precision (2e-14%).

E.g. if you try to add $100 trillion + $0.01 using dinero.js, you still get underflow and the $0.01 disappears.

Post reply on HN