Live data from Hacker News

Your balance is $0.30000000004

medium.com

21–30 of 69 posts

Re: Your balance is $0.30000000004

#21
post #16

Work with cents everywhere and format the output in the UI. Done.

This approach doesn't work when dealing with fractional cents, like charging $0.0001 per hour for something. I would rather recommend using an arbitrary precision decimal library like Decimal.js, and using strings to represent money in JSON. And of course using an appropriate database type for storing the data.

Take the inverse and use fractional hours per penny.

Edit: And sum your (optionally price-weighted) time first.

Re: Your balance is $0.30000000004

#22
post #2

You would think that this is a fairly rookie error and that big companies would know better, but I regularly see this on Uber: https://i.imgur.com/qDACtG0.png

Uber does not use float for money but the internal representation is converted to something displayable in the API layer.

Normally strings are used, for example: https://developer.uber.com/docs/riders/references/api/v1.2/r...

Guess somebody messed up for this case.

Re: Your balance is $0.30000000004

#23
post #2

You would think that this is a fairly rookie error and that big companies would know better, but I regularly see this on Uber: https://i.imgur.com/qDACtG0.png

browser console: (1.005).toFixed(2) (1.005).toFixed(20) reveals the problem. Math.round(1.005 * 100) // wrong In the end these conversion errors are not solvable in any language, so you have to "cut off" somewhere. There are different approaches to this. Wasn't there a case where programmers stole the "wrong" cent and wasn't The Office a persiflage on that?

> In the end these conversion errors are not solvable in any language

Not true at all.

Ruby has BigDecimal: https://ruby-doc.org/stdlib-2.5.1/libdoc/bigdecimal/rdoc/Big...

.NET also has a Decimal type.

I've worked on salary calculation applications and e-commerce platforms, and found that language choice makes a big difference.

Re: Your balance is $0.30000000004

#24
I'm currently rewriting our store implementation, and it's complicated.

One example is that some currencies have 100 subunits (eg USD), some have 10 subunits (EG TWD/MOP) and some have no subunits (eg JPY). This is made slightly more difficult in that Stripe/Paypal will treat subunits differently, for example Paypal might treat JPY as having 100 subunits and Stripe will treat it with 0 subunits.

You want to be really careful here not to charge 10x/100x more/less to the end customer than you imagine.

Formatting currencies is also something you need to do carefully as dots and commas mean completely different things in various countries and can lead to unexpected charges for customers.

Don't get me started on VAT :P

All worth it in the end though, I enjoy writing this sort of thing and I do see long term benefit to our business that we do it all in-house.

I'd also recommend future proofing implementations to use longs instead of ints for storing money values. Back when Bitcoin was less valuable we allowed BTC as a currency where 1 subunit = 1 satoshi (100,000,000 satoshis in a bitcoin). It was possible to overflow an int this way.

You also can't really write payment systems with the speed you might write other code in your startup - errors and mistakes here could have real impact on peoples lives. For example, loops that create charges that don't end (possible with both Stripe AND Paypal). Loops can manifest themselves as lock collisions as well. Basically, be careful and if speed is important you probably want to outsource it at the expense of some control.

Re: Your balance is $0.30000000004

#25
post #2

You would think that this is a fairly rookie error and that big companies would know better, but I regularly see this on Uber: https://i.imgur.com/qDACtG0.png

browser console: (1.005).toFixed(2) (1.005).toFixed(20) reveals the problem. Math.round(1.005 * 100) // wrong In the end these conversion errors are not solvable in any language, so you have to "cut off" somewhere. There are different approaches to this. Wasn't there a case where programmers stole the "wrong" cent and wasn't The Office a persiflage on that?

The story I heard is that the first time this was attempted in real life, the account balance grew so fast that it attracted attention and they were caught.

Re: Your balance is $0.30000000004

#26
post #23

Earlier quoted context omitted.

browser console: (1.005).toFixed(2) (1.005).toFixed(20) reveals the problem. Math.round(1.005 * 100) // wrong In the end these conversion errors are not solvable in any language, so you have to "cut off" somewhere. There are different approaches to this. Wasn't there a case where programmers stole the "wrong" cent and wasn't The Office a persiflage on that?

> In the end these conversion errors are not solvable in any language Not true at all. Ruby has BigDecimal: https://ruby-doc.org/stdlib-2.5.1/libdoc/bigdecimal/rdoc/Big... .NET also has a Decimal type. I've worked on salary calculation applications and e-commerce platforms, and found that language choice makes a big difference.

It's not that they're not solvable, it's just that you have to go out of your way to use the proper type or library. The default floating point type still has problems.

Re: Your balance is $0.30000000004

#27

I'm currently rewriting our store implementation, and it's complicated. One example is that some currencies have 100 subunits (eg USD), some have 10 subunits (EG TWD/MOP) and some have no subunits (eg JPY). This is made slightly more difficult in that Stripe/Paypal will treat subunits differently, for example Paypal might treat JPY as having 100 subunits and Stripe will treat it with 0 subunits. You want to be really…

What's the long term benefit?

Re: Your balance is $0.30000000004

#28
post #4
post #2

You would think that this is a fairly rookie error and that big companies would know better, but I regularly see this on Uber: https://i.imgur.com/qDACtG0.png

On the surface one would think big companies should "know better". I mean, a large company is the accumulation of centuries or even millennia of career experience. Unfortunately, they also contain just as much accumulated nonsense and foolishness; and enough bureaucracy and organizational cruft to hide it forever.

A large company is the accumulation of a large number of people, some of who will know better and some of who will not. Hopefully the company is organized such that the people who know will be in a position to catch those kind of errors, but that's not guaranteed.

Re: Your balance is $0.30000000004

#29
post #9

Good example of a leaky abstraction. JavaScript developers almost never have to worry about or completely understand the intricacies of floating-point arithmetic, until errors like this happen. https://en.wikipedia.org/wiki/Leaky_abstraction

This is not a leaky abstraction. JavaScript doesn't pretend that the numeric type is anything other than an IEEE-754 float. It would be a leaky abstraction if JavaScript said "this type can represent all real numbers!" but that would be insanity.

Sure, it's not a leaky abstraction as long as what you want to represent is an IEEE-754 float. Let me know if you ever come across a real-world case where that's exactly what you want.

Sure, that's a good-enough abstraction for a lot of cases, which is why a lot of languages implement that standard, and it's pretty reasonable for JS to do that.

The pedantic argument you're making is basically that it was never intended as an abstraction. But when it's used as an abstraction, it's almost inherently a leaky abstraction, and it's almost always used as an abstraction.

Re: Your balance is $0.30000000004

#30

Martin Fowler's money pattern [0] solves this problem. Just make sure whenever you're dealing with currencies to use a value object for money, rather than float. Lots of languages have libraries for this pattern. [1] [0] - https://www.martinfowler.com/eaaCatalog/money.html [1] - https://github.com/topics/fowler-money-pattern

One thing I've always wondered is who the heck is Martin Fowler and why does he have such a following?
Post reply on HN