Live data from Hacker News

Your balance is $0.30000000004

medium.com

41–50 of 69 posts

Re: Your balance is $0.30000000004

#41
Obligatory reference to the Moonpig billing system, and Mark Dominus' excellent deep-dive article on its design:

> Sometimes I see other people fuck up a project over and over, and I say “I could do that better”, and then I get a chance to try, and I discover it was a lot harder than I thought, I realize that those people who tried before are not as stupid as as I believed. That did not happen this time. Moonpig is a really good billing system. It is not that hard to get right. Those other guys really were as stupid as I thought they were.

https://blog.plover.com/prog/Moonpig.html

Re: Your balance is $0.30000000004

#43
post #18
post #16

Earlier quoted context omitted.

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.

Just make the smallest possible fractional cent the unit then

That works if you are 100% sure you know beforehand what the smallest possible fraction will be.

What you gain by consistently using an arbitrary precision decimal library to process all monetary values, is the freedom to process smaller fractions later if necessary. Also you'll never overflow with really big values.

In other words, when you represent monetary values with arbitrary precision Decimal objects everywhere, you can easily expand your application as much as you want on either side of the decimal dot. Changing existing integer-based code do to that is very error-prone, you quickly lose sight of what exactly an integer value or parameter means in different places.

Re: Your balance is $0.30000000004

#45
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.

Important caveat: BigDecimal is arbitrary-precision, not infinite-precision. Using it still requires you to think about the precision of each number you operate on.

Re: Your balance is $0.30000000004

#46

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…

Not to mention the pain if you're selling low value (sub-penny) items, that get scaled by weight or some other dimension. The errors can often throw out invoice totals in the most annoying manner.

Re: Your balance is $0.30000000004

#47

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

Working in cents isn't always sufficient. Let's do 34.7% of $25. In cents, $25 is 2500. 34.7% of that is exactly 867.5 cents, which should round to 868 cents under the most common rounding methods in most commercial applications (round to nearest with 0.5 rounding up, or round to nearest, with 0.5 rounding to even).

In JavaScript:

  > p=2500; r=0.347;
   Math.round(p*r)
  
You need to integerize the percentage, too. For my current financial applications, all the percentages I need are integer multiple of 0.01%, so I use that as my "percent cent". 34.7% is then represented as 3470.

That leads to a function something like this (assuming you need the 0.5 round up rule...changing it to 0.5 rounds to even is left as an exercise):

  function percent(amt2, rate4)
  {
    return Math.floor((amt2 * rate4 + 5000)/10000)
  }
(The 2 and 4 suffixes are a naming convention I use. They are reminders that amt2 is the underlying amount x 100, and that rate4 is the underlying rate x 10000).

Re: Your balance is $0.30000000004

#48
post #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?

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

You don't know the history of your craft; study more, look into where agile came from, the extreme programming movement, and who was involved and how refactoring became a thing. Names like Ward Cunningham, Kent Beck, Martin Fowler, Ron Jeffries, Rob Martin, and Dave Thomas should be familiar to anyone who knows their craft.

Re: Your balance is $0.30000000004

#49

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…

In Tunisia is 0.000 At least it was. Not sure alot now

Re: Your balance is $0.30000000004

#50

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

Really the only foolproof method is to use rationals - numerators and denominators.

Was going to say this too, specifically rationals with arbitrary precision should cover all general currency related issues assuming irrational values are forbidden.
Post reply on HN