Hacker News new | past | comments | ask | show | jobs | submit login

I haven't worked in fintech but I've read that money is often represented (at least in storage) as plain integers, since for example US currency only ever goes to two decimal places. But I guess once you start operating on it you run into potential truncation unless you use rationals.

In finance, US dollars are generally stored to four decimal places, because you need to deal with stuff like compounding interest or stock splits.

COBOL has a built in fixed point integer type, which makes defining a 4 digit decimal and doing math on it easy. (IBM designed it from the ground up to cater to people with a lot of money, who spend a lot of money, to work with lots of money, ie banks) Java has the BigDecimal type, which is a class in the class library, which means you need to import it. And because Java lacks operator overloading, doing calculations is tedious.

In the 90s, there was a huge push to replace COBOL with <something else>, and Java was the Rust of its day, so that's what everyone got behind. However, 4 digit COBOL decimals apparently round differently than 4 digit Java BigDecimals, so all the tests failed. And all the stuff like a\x+b had to be written like BigDecimal.add(BigDecimal.multiply(a,x),b) so development was taking forever.

Eventually they said "fuck it" and 20 years later we're still stuck with COBOL and everyone who remembers the original death march says "never again".

I have a feeling a lot of the problems came down to computer science people thinking money has two decimal digits but domain knowledge people knowing it has four. We programmers, as a group, make a lot of assumptions about other peoples' domains and we're wrong a lot*.

I've had the thought that programmers should note assumptions in flagged comments, and those comments should be automatically collected, and then reviewed occasionally. Assumptions might be sustainable, so to speak, but they can also create one kind of technical debt.

> make a lot of assumptions about other peoples' domains and we're wrong a lot

What do you mean this person has no surname? That's unpossible, surname is never null, error error.

US currency can go to more than two decimal places...


I guess it's time for someone to write an "Assumptions Programmers make about money" post.

Falsehoods programmers believe about prices: https://gist.github.com/rgs/6509585

Interesting list, though I'm not sure what do they mean by n. 7

For a brief time in 2008, 1 Zimbabwe dollar was very roughly equivalent to one TRILLIONTH of a United State penny. So technically a value of “1” did exist, but it was meaningless. I have some of the 100 Trillion Dollar notes from Zimbabwe from that time period.

I also have a few. You could buy a stack of 100 trillion dollar bills few a few bucks then. They are now selling for $50-$60 on eBay.

Investing in ZWL, bold move!

1. Money in a brokerage account is not US currency.

While it isn't physical US currency, my brokerage account represents the value of the account in units of USD- therefore any rules about how US currency works should apply.

Additionally, fractional cents are often presented to the consumer when purchasing gas/fuel.

Money is not as wierd as anyone might guess. I work on a financial application, and money is almost always just a BigDecimal with the scale set to 2 (and stored in a database as a bigint type or equivalent). When its not, its just a higher scale (for say, compound daily interest on small amounts for a significant period of time).

How do you store Bitcoin?

Bitcoin uses fixed-width unsigned integers. The smallest unit is 10^-8 of 1 Bitcoin, which is represented as just 1.

I like this approach. Is it compatible with the parent comment?

Yes, the parent approach is the same thing but with a decimal point added for convenience. The main thing is that the scale is fixed; it is effectively an integer count of 1s of the least significant index. It is impossible to truncate values or round upward and create money that didn't exist. This makes it perfect for representing actual money.

No it can't. There are systems that track things worth less than a penny for later billing, but at the end of the month when they bill someone, they do some sort of rounding.

If you are earning interest at a bank, and you've earned a fraction of a penny, they will eventually pay it to you once you've earned enough for a whole penny.

i.e. they track your account balance to more than 2 digits, they just only show you 2 digits.

Someone should tell that to everyone who ever used a ½¢ coin in the US. Also, US law explicitly states (31 USC §5101) that the unit of 1/1000th of a dollar is a mill.

>Someone should tell that to everyone who ever used a ½¢ coin in the US.

All 10 of them?

I’ve got some at home, but admittedly I’d never use one as currency. I also have US ½¢ paper notes too.

Go to a bank and ask for a half penny or a thousandth of a dollar. Let me know how it goes.

Go to a bank and ask for a $500 or $1000 note too. They won’t give you one as they aren’t in circulation anymore, but most (all?) will let you deposit it for face value.

Because, unlike a thousandth of a dollar, those are valid amounts for real transactions.

It goes without saying that half pennies are dead. A mill seems to be from the Coinage Act of 1792, which is perhaps a tad outdated.

Take two half penny coins or notes to the bank and they’ll credit your account a penny. Of course, just like with the $500 or $1000 notes, you’ll be losing money on the deal.

> A mill ... is perhaps a tad outdated.

Yet you use mills every time you pay for gas. Pointless in that case? Probably. Still used all the time? Certainly.

I pull the gas lever intermittently until the gal goes up but the cents don't. It takes about 5 tries, but always makes me smile that I beat the game.

>>...since for example US currency only ever goes to two decimal places

That is not correct. Stock settlement transactions often list four decimal places.

> Stock settlement transactions often list four decimal places.

That's not a significant difference compared to two decimal places, so brundolf's point still stands. There's no need for arbitrary precision.

Just store all dollars in PIPs so 5$ will be stored as 50000.

You can still get reasonable enough approximations with more than two decimals if you do something like `int64 myWorkingMoneyVal = currentMoney * 100000`, do your work, then divide the final result by 100000. You still risk some potential truncation if your work involves division, but the larger your multiplier that you're working with, the larger divisor at the end, which will help minimize how much of an error this ends up being. The 64 bit integer space is pretty darn big, so you typically don't risk an overflow, and you will typically get better performance than using a regular "decimal" type, since on-chip integer operations are usually very fast.

EDIT: Just a note, there's nothing special about the number 100000; pick the largest exponent of 10 that you can get away with a reasonable assurance that no overflow is possible. For a vast majority of money applications, I seriously doubt you're going to be hitting the limits of int64, so you could probably even get away with something like 1000000000.

Google uses 1000000 as multiplier in their APIs.

Edit: And they forbid equality comparisons for rationals. For some reason even >= is not allowed.

I didn't know that, but it doesn't surprise me (I suspected I wasn't the first person to come to the realization that there's no reason not to choose a giant number :) ).

I have developed a payment plan calculator for asset based finance and you would be amazed how many different rounding schemes and day counters (for fractional periods) exist and are actively used.

Counterexample: gas prices in the US are frequently displayed with 3 decimals (tenth of cents)

It isn't really a price though, it is a rate for an infinitely divisible good, i. e. $/L. You get the price when you multiply with the quantity purchased.

So how much do 2 CCs of gasoline cost?

The basic representation is usually good enough at 2 decimals (so a plain int), but it is often needed to have a transient representation during calculations.

For instance if one needs to apply discounts, add taxes, split in equal parts, all of the above one after the other, there will be a more precise intermediary representation before rounding everything in a way that keeps the total amount consistent with the original amount.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact