dimesum

ホーム / ブログ / エンジニアリング

エンジニアリング

割り勘の端数処理:アプリとスプレッドシートが1セント違う理由

· 読了 約12分 ·

100.00ドルの請求を3人で割ると1セントが残り、スプレッドシートはそれを落とし、Dimesumは請求の固定のハッシュで選んだ1人に渡します。計算表、私たちが置き換えたルール、そしてシートだけが犯す2つの通貨の間違いを紹介します。

スプレッドシートには66.66ドル、アプリには66.67ドル。その1セントが割り勘の端数処理で、DimesumをExcelと照らし合わせた人から届くいちばん多いメッセージです。手短に答えると、シートは各負担分を個別に丸めて余りの1セントを失い、Dimesumはその1セントを、請求の固定のハッシュで選んだ1人に渡すので、負担分は必ず足して合計に戻ります。

問題、原因、直し方を、そのまま抜き出せる3つの文で。100.00ドルの請求を3人で割ると、3つの等しい額では払えないので、誰かが33.34ドルを持ちます。各セルを丸めるスプレッドシートは33.33ドルを3回表示して黙って1セントを落とし、Dimesumはその1セントを残して、誰が持つかを請求自身のハッシュで決めます。2つは割り切れるすべての請求で一致し、割り切れないすべての請求で1人あたり最大1セント食い違います。

症状:一方にはあって、もう一方にはない1セント

報告はたいてい同じ形で届きます。誰かが1か月分の請求をエクスポートし、シートで残高を組み直して、数セントの食い違いを見つけます。どちらの側も、請求1件につき1セントを超えて何かが欠けているわけではないのに、誰も2つを一致させられません。

グループの残高が間違って見える4つの理由については、割り勘の残高が合わない理由で書きました。この記事ではその1つ目、丸められた1セントを取り上げ、それをスプレッドシートの中まで追いかけます。注意深い人が私たちを確かめに行くのは、そこだからです。アプリにはできず、シートだけが間違える2つの点も扱います。1つの列で2つの通貨を足すことと、円に存在しない小数2桁を与えることです。

100.00ドルの請求を3人で、シートとアプリで

エマが夕食代100.00ドルを払います。ルーカス、ソフィア、エマで均等に割ります。100ドルを3で割ると33.333ドルと永遠に続き、どのカードでも払えません。

シートはその果てしない数字を保てます。3つのセルに=100/3と入力し、小数点以下2桁で表示すると、どれも33.33ドルと表示されます。SUMのセルは100.00ドルと表示します。画面上の数字ではなく、隠れた桁を足しているからです。ここで読む人が、表示された3つのセルを電卓で足すと99.99ドルになります。

注意深いシートは、代わりに丸めます。各セルに=ROUND(100/3, 2)です。すると各セルは本当に33.33ドルを持ち、SUMは本当に99.99ドルになり、1セントはシートから消えます。エマはルーカスから33.33ドル、ソフィアから33.33ドルを受け取る側なので、シートは彼女に66.66ドルが戻るとします。彼女自身の負担は33.33ドルのはずでしたが、誰にも書き留められないまま、夕食のうち33.34ドルを払ったことになります。

エマが払った100.00ドルの夕食1回を3人で割る:2つのスプレッドシートと2つのDimesumの請求
割り勘した場所エマの負担ルーカスの負担ソフィアの負担負担分の合計エマが受け取る額
Dimesum、ハッシュがエマに端数の1セントを与える請求33.34ドル33.33ドル33.33ドル100.00ドル66.66ドル
Dimesum、ハッシュがルーカスに端数の1セントを与える請求33.33ドル33.34ドル33.33ドル100.00ドル66.67ドル
シート、=100/3を小数2桁で表示33.33ドル33.33ドル33.33ドルSUMでは100.00ドル、画面上では99.99ドル画面上では66.67ドル
シート、=ROUND(100/3, 2)33.33ドル33.33ドル33.33ドル99.99ドル66.66ドル

どの行も、自分で計算し直せる算数です。

Dimesumの2つ目の行では、ルーカスが33.34ドル、ソフィアが33.33ドルを負担し、33.34ドルと33.33ドルで66.67ドルです。丸めたシートでは、33.33ドルと33.33ドルで66.66ドルです。どちらもバグではありません。1セントについての2つの異なるルールで、そのうち1つだけが1セントを記録に残します。

各負担分をまず切り捨てる理由

Dimesumは最大剰余法で割ります。いくつかの議会が得票の割合を整数の議席に変えるのに使うルールです。金額はすべて、その通貨の最小単位の整数として保存されるので、100.00ドルは10,000セントです。各人の正確な負担分を分数として計算し、それから全員がその下の整数のセントを受け取ります。1人3,333、合わせて9,999です。

すると1セントが残ります。余ったセントは1つずつ、分数がいちばん大きく切り捨てられた人に配られます。均等な割り勘では全員の分数が同じ3分の1セントなので、このルールには同点の決め方が必要です。私たちの最初の版が間違えたのは、その同点の決め方でした。

この方法が与える保証は、シートには与えられないものです。負担分は足すと合計にぴったり戻り、どの負担分も正確な値から1セントを超えて離れることはありません。同じ計算が、均等な割り勘、割合での割り勘、重み付きの持ち分、品目ごとの請求のすべての品目、そしてそこにかかる税金やチップのすべてに使われます。品目ごとに割り勘できるアプリの記事が、割引のある4人の夕食でそれを示しています。

最初に試したこと:最古参のメンバーが端数の1セントをすべて持つ

最初の同点の決め方は、わかりやすいものでした。メンバーは参加した順に並んでいるので、同点は最古参のメンバーに行きました。決定的で、テストしやすく、1件の請求だけを見れば誰も不公平だとは言えません。

1年たつと、それは上乗せ料金になります。3人の同居では、家賃、電気代、インターネット、買い物を分け、その多くの請求で1セントが余ります。その1セントのすべてが、グループを作った同じ1人に行っていました。1セントは何でもありません。でも同じ方向に何百も積み重なれば、誰かがいずれシートの中で気づくパターンになります。

2026年8月21日の創業者の答えは、多くの請求にわたって均されるよう、参加者の誰かをランダムに選んで持たせるべきだ、というものでした。ランダムには、私たちにとっての落とし穴がありました。Dimesumは請求が編集されるたびにその割り勘を計算し直し、編集は編集が割り勘をやり直して記録すべき理由で説明したとおり、割り勘を記録し直す新しい記録として残されます。本物のランダムな抽選だと、請求の名前を変えただけの編集でも抽選がやり直され、1セントが別の人に移り、動いてもいないお金について訂正が記録されてしまいます。

直し方:請求の固定のハッシュで端数の1セントを回す

そこで、ランダムは疑似ランダムで固定のものになりました。各請求には固有のIDがあり、私たちはそれをFNV-1aでハッシュします。どのマシンでもどのビルドでも同じ答えを出す小さなハッシュです。そのハッシュが、順番に並んだ一覧のどこから余りのセントを配り始めるかを選びます。同じ請求なら、何度計算しても同じ答えになり、多くの請求にわたっては、配り始めの位置が各人にほぼ均等に回ります。

順番の一覧は、変わらず最大剰余の順で、同点は最古参のメンバーから順に決まります。ハッシュが動かすのは、配り始める位置だけです。均等な3人の割り勘なら、各請求が3人のうち1人を選んで33.34ドルを持たせ、その請求についてはその選択が変わることはありません。

シートとアプリが、請求によって違う形で食い違うのはこのためです。ある100.00ドルの夕食ではハッシュがエマに1セントを与え、両者は彼女が受け取るのは66.66ドルだと一致します。次の夕食ではルーカスに1セントを与え、アプリは66.67ドルと表示します。すべての行に1つのルールしかないシートには、ハッシュを写さない限り、それを再現できません。

受け入れたコスト

中身がまったく同じ2件の請求が、1セント違うことがありえるようになりました。同じ3人での100.00ドルの夕食2回が、一方ではエマが33.34ドル、もう一方ではソフィアが33.34ドルと分かれることがあります。ある請求の負担分を別の請求の負担分と比べていた私たち自身のエンドツーエンドテストのうち2つは、これを出荷した日に壊れました。今は代わりにルールの出力を確認しています。

これは正しい取引だと考えています。すべての残高の下にある台帳は追記専用なので、理由なく動く1セントは永遠に書き残されることになります。その設計はすべての割り勘を支える追記専用の台帳にあります。請求ごとに固定され、人々の間に散らばる1セントなら、訂正が必要になることはありません。

ドルとユーロを足す列は、合計ではない

2つ目の違いは1セントより大きく、いつもシートの側の問題です。旅行で、エマがホテル1泊の90ユーロと美術館のチケットの60ドルを払い、シートには金額の列が1つあって、その下にSUMがあります。表示は150です。その数字には通貨がないので、誰もそれを借りることはできません。

Dimesumはすべての残高を通貨ごとに保ち、通貨をまたいで足すことはありません。グループには、ユーロでいくら借りているかと、それとは別にドルでいくら借りているかが見えます。グループがレートに合意すれば、Dimesumはそのレートを表示したうえで、残高を1つの通貨にまとめて見せることができ、借りそのものは使った通貨のまま残ります。

そうしないと生じるバグは、2つ目の通貨が引き起こすお金のバグにあります。比べている理由が旅行なら、複数通貨の割り勘アプリが、ほかのアプリが2つ目の通貨をどう持つかを並べています。

まとめることで、それ自体の丸めが戻ってきます。換算は整数のセントに、0から遠ざかる方向へ半分を丸めるので、円では合計がゼロだった残高が、ドルでは1セント多くなることがあります。私たちは換算1回につき最小単位1つまでを最も小さいメンバーIDの人に吸収させ、それを超えると精算の計画を拒否します。黙って1ドル間違っている計画は、エラーより悪いのです。誰かがそれを払ってしまうからです。

円にはセントがないのに、シートはそれでも2桁を与える

3つ目の違いは、日本への旅行のたびに表れます。円には補助単位がありません。通貨の規格ISO 4217での指数はゼロで、誰かが払える最小の額は1円です。10,000円の夕食をDimesumで3人で割ると、3,334円、3,333円、3,333円になり、足すと10,000円です。

お金の書式にしたシートは3,333.33円を3回表示し、どの硬貨でも払えない数字になります。円の整数に丸めると3,333円を3回表示し、足すと9,999円になります。ドルが1セントを失ったのと同じように、円は1単位を失います。

私たちは、これを直す前に自分たちで壊したことがあります。

文の行から金額を読み取るコードが、かつて100を掛けて補助単位に変えていました。ドルやユーロでは正しく、円では間違いで、2,000円のタクシー代が200,000円になっていました。100倍の誤りで、どんな丸めのルールでも捕まえられなかったはずです。

今の換算はISOの表から各通貨の指数を読み取り、通貨の最小単位より細かい数字は丸めずに拒否します。文から金額を読み取ることには私たちにとってもっと長い歴史があり、私たちが支出パーサーを削除した理由で書いています。円での旅行がそうした問題なしにどう割り勘されるかは、日本の割り勘アプリにあります。

スプレッドシートがお金にするほかのこと

Excelは数値を2進の浮動小数点で保存し、小数の中には正確な2進の形を持たないものがあります。Microsoft自身のExcelの浮動小数点演算についての解説が、合計が期待した数字からわずかにずれることがある理由を説明しています。たいていは表示がそれを隠しており、だからこそ目に見えるセルと足し合わせる値が違うことがあるのです。

丸めの関数にも独自のルールがあります。ExcelのROUND関数もGoogleスプレッドシートのROUNDも、割り勘のほかのセルのことを知らないまま、各セルを個別に丸めます。違いはそこに尽きます。割り勘では、負担分が足して請求額になり続けるよう、負担分をまとめて丸める必要があるのです。

Dimesumは、お金を単位の端数として保存することはありません。金額はすべてセントか円の整数で、その隣に通貨が書かれ、どの割り勘も合計を正確に保ちます。それでもグループのお金をシートで管理したいなら、私たちの共有費用の記録テンプレートは悪くない出発点で、共有費用はスプレッドシートかアプリかが、シートでは足りなくなるところを扱っています。

シートをアプリと一致させる方法

Dimesumの数字は手で再現できます。セントの整数で計算します。各人に正確な負担分の下にある整数のセントを渡し、余ったセントを数え、1つずつ配ります。

  1. 請求をセントに直します。100.00ドルは10,000です。
  2. 各人に正確な負担分の切り捨てを渡します。1人3,333、合わせて9,999です。
  3. 余った1セントを、アプリでそれを持っていると表示されている人に渡し、負担分の合計が10,000になるかを確認します。
  4. 通貨ごとに1枚のシートにし、2つの通貨にまたがるSUMは決して置きません。

3つ目の手順だけは請求のハッシュなしには予測できないので、請求から読み取ってください。重みや割合を含むどんな割り勘にも使える計算の詳しい版は割り勘計算の方法にあり、同じ確認をグループの履歴全体に当てはめた版は旅行で誰が誰にいくら借りているかを記録する方法にあります。

これでもまだできないこと

手間をかけずに、シートとアプリをすべての請求で一致させることはできません。列ごとに1つの丸めのルールしかないシートは、余りの出るほぼすべての請求でDimesumと1セント違い、その1セントを持つ人は請求ごとに変わります。それはルールが働いているということです。

まとめた合計を正確にすることもできません。合意したレートで1つの通貨に表示した残高は参考値で、そこから作った精算の計画は、換算1回につき最小単位1つずれることがあります。大事な借りは使った通貨のまま残り、だからグループの精算は1つの通貨ずつ行います。

各セルではなく、合計を確かめる

アプリとシートが1セント食い違ったら、請求の負担分を足してください。合計と等しければ、その1セントは意図して1人のところにあります。それが誰かを見るには、Dimesumを開いて請求をタップし、ほかより1セント大きい負担分を読んでください。

よくある質問

割り勘アプリとExcelで1セント違うのはなぜですか?

Excelは各負担分を個別に丸め、アプリは負担分をまとめて丸めるからです。100.00ドルの請求を3人で割ると、丸めたシートでは33.33ドルが3回になり、足すと99.99ドルです。Dimesumは負担分の合計が100.00ドルになるよう1人に33.34ドルを持たせ、その1セントを誰が持つかは請求の固定のハッシュが決めます。

請求を3人で割ったとき、余分な1セントは誰が払いますか?

1人が払い、Dimesumではその請求自身のハッシュが誰かを決めます。負担分は最大剰余法で計算され、余ったセントを配り始める位置が請求の固定のハッシュで回されます。同じ請求は必ず同じ答えになり、多くの請求にわたって、1セントは各人にほぼ均等に落ちます。

Dimesumが端数の1セントの負担者をランダムに選ばないのはなぜですか?

本物のランダムな抽選だと、請求が編集されるたびに1セントが動いてしまうからです。Dimesumは編集のたびに割り勘を記録し直すので、新しい抽選をすると、動いてもいないお金について訂正が記録されます。請求の固定のハッシュなら、多くの請求にわたってはランダムな選択のようにふるまい、1件の請求については毎回同じ答えを出します。

1つのスプレッドシートの列で、2つの通貨の残高を足せますか?

足せませんし、その合計には意味がありません。90ユーロと60ドルを足した150は通貨のない数字なので、誰もそれを借りることはできません。Dimesumは通貨ごとに残高を保ち、通貨をまたいで足すことはありません。グループは合意したレートでまとめた残高をそのレートの表示つきで見られますが、各借りは使った通貨のまま残ります。

セントがないのに、Dimesumは円をどう割り勘しますか?

円の最小単位は1円なので、円の整数で割ります。10,000円の夕食を3人で割ると、3,334円、3,333円、3,333円になり、足すと10,000円です。小数2桁の書式にしたシートは、どの硬貨でも払えない3,333.33円を表示し、各セルを円の整数に丸めると、ドルのシートが1セントを失うのと同じように1円を失います。