西班牙贸易商为 GnuCash 数据库设计树立了标准。
Spanish traders set the standard forGnuCash database design

原始链接: https://handson.money/blog/2026-06-06-horse-arse-and-design/

在软件中实现商品支持功能,揭示了金钱管理远比简单的十进制算术复杂。虽然现代系统通常将货币存储为“最小单位”(整数)以避免浮点舍入误差,但历史系统常使用分数。 GnuCash 设计于 90 年代末,其遗留架构基于分数。这种设计源于 17 世纪的西班牙贸易体系,该体系使用八进制增量,因为交易员在计数时会排除大拇指。这种做法在纽交所一直持续到 2001 年。 虽然以分数存储数值在计算上较慢——需要公分母和约分——但它提供了无与伦比的灵活性。它允许动态调整精度,例如在不破坏账本的情况下,将比特币持有量从整币缩放至单个聪。现代系统倾向于固定精度最小单位的速度与简单性,但这牺牲了极高的适应性,而正是这种适应性使 GnuCash 在几十年后依然能够正常运行。归根结底,这种模仿 16 世纪手指计数的“过时”决策,在处理多样化且不断演变的商品单位时,实则是一种虽慢但极其天才的解决方案。

Hacker News | 最新 | 往日 | 评论 | 提问 | 展示 | 招聘 | 提交 | 登录 西班牙交易员为 GnuCash 数据库设计树立了标杆 (handson.money) 15 分 | vitalikpie 发布于 33 分钟前 | 隐藏 | 往日 | 收藏 | 讨论 | 帮助 指南 | 常见问题 | 列表 | API | 安全 | 法律 | 申请 YC | 联系 搜索:
相关文章

原文

June 6, 2026 • Author: Vitalik

TLDR: Spanish traders in the 1600s did not want to count their thumbs, which influenced GnuCash's database design choices in 1997. Much like a horse's arse set the standard for railways. How seemingly odd design turns out to be a genius solution that serves people pretty well.

It's one of those nights. A cup of hot coffee is on the table. I'm implementing a commodities support in HandsOnMoney which looks absolutely trivial on the surface, but is quite deep in reality. After all, what can be simpler than currency - there are dollars and cents. What can be simpler than storing a number and calculating totals?

Wait for it. There are multiple layers in this…

Layer one. Cultural.

There is not always 100 cents in a dollar. Well, there is. But other currencies may have no "cents" or have a thousand "cents". Those "cents" are generally called "minor units".

Here are a few concrete examples:

  1. Japanese Yen has no minor units (due to post-WWII inflation)
  2. The Kuwaiti Dinar has a thousand minor units (this allows the country to use smaller increments of value and maintain precise pricing for trade)
  3. And Bitcoin has 100 million Satoshis!

But wait for it. There are even historical currency- real de a ocho that could be divided into 8 pieces! So 1/8th of a coin was the smallest indivisible unit!

Which brings us to the next layer - how in the world do we store it?

Layer two. Software.

Computers are not good with fractions. There are abstractions: float and double types. But they are approximations rather than real fractional numbers. The late 90s and early 2000s brought us decimal, which is a much better type; however, it was likely not available when GnuCash was released.

There is a lot written about it. Usually, developers use a double numeric type to record and store the floating-point numbers. But this solution for recording floating-point numbers does not play well with money.

For example, it is possible that.

1.03 - 0.42 = 0.6100000000000001

Which seems like a trivial rounding error. But over a large number of transactions, the rounding error will accumulate, and you'll eventually see a weird account balance.

In addition, money has a fixed precision and specific rounding rules. So software engineers stepped on this landmine so many times that they developed a common pattern - Money. Which, in a nutshell, is just storing minor units in whole numbers since computers are pretty good with them. So instead of recording $5.23, the money is stored as 523 cents.

Problem solved? As you'd expect - no.

Layer three. Historical.

It's not about money. Well, it is. But it is not only about money. It's also about commodities. In a nutshell, money is a commodity. People trade various commodities - currencies, stocks, and funds.

This generalization needs us to go back in the past. And…

Party like it's 1998

It's 1998. The year of the first release of GnuCash (xacc at that time). Dotcom money is pouring from the firehose, party everywhere…

But the most important thing - NYSE quotes are not decimal. They are fractional!

GnuCash was released before the decimalization of US exchanges was completed in 2001.

Why? Get back into the time machine and …

… trade like it's the 16th century

When the NYSE launched over 200 years ago, it adopted the Spanish trading system from the 1600s, based on fractions because traders counted gold doubloons on their fingers, skipping their thumbs. That's why the smallest stock increment was 1/8 of a dollar. Not a technical decision. A thumb decision. (source - Investopedia)

Okay, enough of counting fingers in the 16th century. Let's get back to 2026.

Layer four. Data Engineering.

In 2026, everything is traded in decimals. We are counting fingers and thumbs.

Is GnuCash's design decision to store fractions an artifact from the past?

It already caused a bug

The register in 2.7.8 displays fractional price for commodity accounts. This is a bit confusing to look at as my brain is too slow process the division of i.e. 1250 with 2449. See https://imgur.com/SA3Kkbh https://bugzilla.gnome.org/show_bug.cgi?id=794755

Mostly yes. GnuCash is full of little quirks from the past (it still supports Italian Lira, which was replaced with Euro in 1999!).

But this seemingly outdated design decision to store fractions works extremely well in a modern world.

Consider this example: if I bought 1 Bitcoin in 2011 for 90 cents. In my GnuCash book, I recorded

Account Debit Credit
2011 Checking Account 9/10 cents
Bitcoins 1 Bitcoin

Now in 2026. I can sell a fraction of my holdings and price it in Bitcoin's minor unit - Satoshi.

I can easily flip the Bitcoin precision in the commodities editor from 1 to 100,000,000, and GnuCash will still calculate the balance without any issues.

For example, here I'm selling 3 Satoshis in 2026:

Account Debit Credit
2026 Checking Account 1$
Bitcoins 3 / 100,000,000 Bitcoins

It's simple, and that's why it's genius!

However. None of the modern systems work this way. Why?

Layer five. Scalability.

Keeping every amount as a fraction is an extremely flexible solution. However, it is slow.

Adding or subtracting? You need to find a least common denominator.

You need to reduce the results back, or you are risking blowing up past type boundaries.

The complexity and performance at scale are much worse. And considering the fact that everything is traded in decimal fractions now, it's much easier computationally to do: 1150 cents + 1075 cents rather than 11 1/2 dollars + 10 3/4 dollars.

How things work in HandsOnMoney?

When I first designed HandsOnMoney, it was a reflection of my limited understanding of currencies. HandsOnMoney was designed to store the amounts in minor units, and each account has its own commodity with fixed precision. This decision still holds to this moment, and the tradeoffs of the fractions still outweigh the simplicity of minor units. In practice, it means some things are not fully compatible with GnuCash:

  1. Non-standard account fractions per account are not supported.
  2. It's not possible to change a commodity's precision on the go.
  3. Obviously, fractional commodities are not supported.

So, unless you are a Spanish trader from the 16th century or have a book with fractional stocks from the 90s, HandsOnMoney will serve you well.

Surprisingly written by a human :)

联系我们 contact @ memedata.com