dimesum

首页 / 博客 / 工程

工程

为什么你的分摊账单总是对不上

· 阅读约 9 分钟 · · 审阅 Jagadeep Sai

对不上的共享余额几乎总能归结为四件事之一:一分钱的舍入、第二种币种、一次被编辑的账单,或一位离开的成员,而只有第一种是算术。

你正看着一个群组的余额,它对不上。也许只差几分。也许差了一整笔账单。背后的疑问很安静,却很真实:是应用算错了,还是你算错了?

冷静的答案是这样的。当你和别人分摊账单时,一个对不上的余额几乎总是出于四种原因之一,而其中只有一种和算术有关。另外三种分别是币种、一次修改,以及某个离开的人。弄清楚你遇到的是哪一种,修正就不再神秘。

四种原因

先看清问题的形状。共享余额是一份关于谁付了钱、谁欠着钱的累计记录。它在整个群组里应当加总为零:有人被欠的每一元,就是另一个人欠着的每一元。当它不为零时,说明有某样东西进入了这个总额,而总额容纳不下它。

那么分摊账单为什么会对不上?通常是四种原因之一。当一笔账单无法整除时,有一分钱去了意想不到的地方。两种币种被当成同一种加在了一起。

一笔旧账单在大家已经据此付款之后被改动了。又或者某个人在还欠着钱的时候离开了群组。这四种里有三种和你的算术毫无关系。想更全面地了解一个群组究竟如何结清债务,请读我们关于群组结算的拆解。

下面的表格把每种原因、它如何出现、以及如何修正各列一次。表格之后的内容都是详细版本。

共享余额对不上的四种原因

共享余额对不上的四种原因
原因如何出现如何修正
舍入余额只差一两分,从不更多一条确定性规则,总是把多出的那一分送到同一个地方
币种总额差了一整笔金额,或者显示为一个奇怪的数字每种币种各记一份余额,绝不跨币种相加
修改一笔旧账单被改动后,余额跳变重述这笔账单的分摊,并把差额记入历史
有人离开无论如何余额都无法归零决定由谁承担或免除离开者的份额,然后记录下来

舍入:多出的那一分,以及为什么它必须是确定性的

例子如下,其中每个数字你都可以自己重新算一遍。Lucas、Aiko 和 Sofia 一起出去。晚餐是 100.00 元,他们平摊。100.00 元除以三是 33.3333,无限循环下去,任何币种都付不出这个数。于是分摊变成 33.33、33.33 和 33.34。

把它们加回去:33.33 加 33.33 加 33.34 正好是 100.00 元。这三份都是诚实的。其中一份多了一分。

多出的那一分,就是整个问题的缩影。总得有人来承担它。这就是共享支出的舍入,它只有一项任务:把多出的那一分放到一个固定的地方,而不是随机的地方。如果应用每次都重新分配它,同一顿晚餐今天可能让 Aiko 承担 33.34,明天又让 Sofia 承担 33.34,余额就会毫无缘由地晃动一分。

所以我们让规则是确定性的。多出的那一分每次都归付款人。是 Lucas 付的钱,所以 Lucas 承担 33.34,另外两人各承担 33.33。Lucas 被欠的是 100.00 元减去他自己的 33.34,也就是 66.66。

Aiko 欠 33.33。Sofia 欠 33.33。33.33 加 33.33 是 66.66。整个群组加总为零。这次舍入用的是人民币的两位小数,也就是国际货币标准 ISO 4217 为它规定的最小单位;把这一分交给付款人是我们的选择,它唯一的好处是从不改变。

网上的分账计算器能为一顿晚餐做这道除法。更难的是为第一百顿晚餐用同样的方式来做,这也正是我们在公平分摊餐厅账单中所关心的区别。会计们长期争论哪条规则最公平;计算领域常见的默认做法,即四舍六入五成双,写在 IEEE 754 标准里。我们在这里用不到它,因为我们这一分并不是一半。

一个整数分成三份,正好余下一分,唯一的问题是这一分由谁来拿。

一个群组里的两种币种,以及为什么它们不能相加

现在群组里进来了第二笔账单。Aiko 付了一趟出租车,花了 6,000 日元。为了图省事凑一个总额,人们会忍不住把 6,000 日元加到 100.00 元上,显示成一个数字。

那个数字毫无意义。100.00 元和 6,000 日元不是同一类东西,把它们相加就像把距离加到重量上。日元的最小单位有零位小数,人民币有两位,这同样出自 ISO 4217。它们连舍入的方式都不一样。

所以我们为每种币种各记一份余额。在人民币这边,什么都没变:Aiko 欠 Lucas 33.33,Sofia 欠 Lucas 33.33。在日元这边,出租车三人平摊,每人 2,000 日元。Aiko 付了 6,000 日元,所以 Aiko 被欠的是 6,000 日元减去 2,000 日元,也就是 4,000 日元。

Lucas 欠 2,000 日元。Sofia 欠 2,000 日元。2,000 日元加 2,000 日元是 4,000 日元。两份余额,各自加总为零,谁也不冒充谁。如果你想了解这里更深层的出错方式,我们把它们写在了跨币种的金额漏洞里。

对一笔旧账单的修改,以及历史必须发生什么

第三个月。有人发现那顿晚餐记错了:它是 120.00 元,而不是 100.00 元。他们做了修改。看起来已经结清的余额,就是在这里突然变动的,也是许多应用悄悄出错的地方。

幼稚的做法是覆盖旧数字,从头重算。但人们已经按旧的分摊付过款了。如果历史被悄悄改写,谁也看不出自己为什么现在欠得更多。所以这次修改必须重述分摊并记下差额,而不是抹掉过去。120.00 元除以三正好是 40.00 元,这次没有多出的那一分。

每份变成 40.00 元。Lucas 付了 120.00 元,所以 Lucas 被欠的是 120.00 元减去 40.00 元,也就是 80.00 元。Aiko 欠 40.00 元。Sofia 欠 40.00 元。40.00 元加 40.00 元是 80.00 元。

人民币余额从被欠 66.66 变成被欠 80.00,而这 13.34 的差额,正好可以追溯到晚餐多出的 20.00 元:另外两份各涨了 6.67,6.67 加 6.67 是 13.34。我们保留旧的记录,在上面加上更正。为什么一次修改必须重述它的分摊,而不是假装这顿晚餐一直都是 120.00 元,这本身是另一个故事,写在一次修改必须重述它的分摊里。

有人离开了,以及他所欠的钱会怎样

第四种原因是一个人,而不是一个数字。假设 Sofia 离开了群组,却还欠着 40.00 元人民币和 2,000 日元。现在余额无法自行归零,因为它所依赖的其中一个人已经不在了。

这不是算术错误;算术没有问题。群组里出现了一个窟窿,大小正好是 Sofia 那一份。

能做的诚实选择只有寥寥几种,而且它们全都是决定,不是计算。群组可以免除这笔金额,并记录它已被免除。可以有人来承担它,并记录是谁。又或者 Sofia 在离开前结清。

重要的是这个决定被作为一条记录写下来,这样余额显示为零是因为发生了某件事,而不是因为一笔债务被悄悄删掉了。我们从不替你做这个决定;我们记录你所做的选择。同样的逻辑也适用于室友在租期中途搬走的情况,我们在和室友分摊房租里谈到了它。

分摊账单时如何自己核对余额

这一切你都可以手工核对,而且值得知道怎么做。一次只取一种币种,绝不混在一起。对这种币种,把每个人付过的钱全部加起来。再把每个人欠的钱一份一份加起来。

一个人的余额是他付的钱减去他欠的钱。对每个人都这样算,然后把所有余额加在一起。总和必须为零。如果不是,差额会告诉你遇到的是哪种原因。

一两分的差额是舍入;看看多出的那一分落在了哪里,并检查它每次都落在同一个地方。一整笔账单大小的差额,或者一个看起来不可能的总额,通常是有第二种币种被并了进来。自你上次查看以来发生变化的余额,指向一次修改,所以打开这笔账单的历史。

根本无法结清的余额,指向某个离开的人。当你和朋友分摊账单时,余额始终只是算术,所以一个顽固的差额是信息,不是运气不好。如果你更想看到数字,而不是听我们空口而言,我们那篇分摊账单的统计展示了这几种情况各自实际发生的频率。

一个只追加的账本如何应对这全部四种情况

Dimesum 的底层是一个只追加的复式记账账本,而且它是公开的,所以这一切都无需凭信任接受。复式记账意味着每一笔金额都被记录两次,一次记为所欠,一次记为所得,因此两边始终平衡;这条原理比软件更古老,在复式记账词条里有清楚的说明。只追加意味着我们从不覆盖。我们只做添加。

这一个设计就应对了全部四种原因。舍入是确定性的,因为那一分的去向被写进了记账规则,而不是临时决定的。币种保持分离,因为每条记录都带着自己的币种,账本拒绝跨币种相加。一次修改会变成一条新的更正记录,重述那次分摊,于是旧的事实和新的事实都得以留存;我们在用于分摊支出的只追加账本里描述了这套机制。

而一个离开的人会变成一条被记录下来的决定,一条和其他任何记录一样的记录,而不是一行缺失的数据。我们把这一切都记录下来。我们从不持有或转移你的钱;账本是一份记录,结算直接在你们之间进行,用我们能算出的最少笔转账完成。如果你在比较工具,我们关于分摊账单应用对比房租分摊应用对比的笔记,只讲每一个能做什么、不能做什么。

一个对不上的余额,并不是对你们友情或你算术的评判。它是关于你们群组的四个小事实之一。打开 Dimesum,账本会告诉你是哪一个。

常见问题

分摊账单的余额为什么对不上?

几乎总是四种原因之一:舍入产生的一分钱、误加进来的第二种币种、被修改过的旧账单,或一位欠钱离开的成员。只有第一种是算术问题。选定一种币种,把每个人付的钱减去他应摊的份额,差额的大小就能告诉你是哪种原因。

账单无法平均整除时,多出的一分钱算谁的?

由应用的规则决定,只要每次都是同一个说法就行。100.00 元的晚餐三人平摊是 33.33、33.33 和 33.34,多出的那一分必须落在一个固定的地方。Dimesum 把它归给付款人,所以同一顿晚餐永远不会两次算出不同的余额。规则比选哪种更重要。

可以把两种不同币种的余额加在一起吗?

不行,任何诚实的应用都不该这么做。100.00 元和 6,000 日元是不同类的东西,最小单位也不同,把它们相加得到的数字毫无意义。Dimesum 为每种币种各记一份余额,绝不把一种并入另一种。你分别结算每种币种,所以一个群组可以同时欠着人民币和日元。

旧账单被修改后,余额会怎样?

分摊会被重述,差额会作为一条新的更正记录下来,而不是覆盖过去。把 100.00 元的晚餐改成 120.00 元,每人的等额份额就从 33.33 变成 40.00,所欠金额增加 13.34。旧记录仍然可见,更正叠在上面,任何人都能准确追溯余额为什么变化。

有人欠着钱离开群组会怎样?

算术仍然没问题;群组只是多了一个窟窿,大小正好是他未付的那一份。在有人决定如何处理之前,余额无法归零。这笔钱可以被免除、由另一位成员承担,或在他离开前结清,无论哪种都会被记为一条记录。Dimesum 记录你做的决定;它从不替你决定。