少し前、嘖嘖(Zeczec)で「端数切り上げ」機能を見かけました。たとえば会計で 1359 元になったとき、端数切り上げボタンを押すと 1400 元になります。面白い機能ですが、実際に実装してみると、端数切り上げは思ったより難しいことがわかりました。技術面以外の配慮事項もたくさんあります。ここでは、実装中に直面した問題と、その過程、そして最終的な解決策を記録しておきます!
嘖嘖の端数切り上げ機能は、大きく 2 種類の戦略を使っているように見えます。1 つ目は、先頭の桁を繰り上げる方法です。たとえば 1001 は直接 2000 に、3499 は 4000 になり、1000 未満の金額はすべて 1000 元に切り上げられます。2 つ目は幸運数字の考え方で、「端数切り上げ」を繰り返しクリックすると、6666、8888 などの整数ではない数字が出てきます。
私は嘖嘖の幸運数字はおもしろいと思いますが、先頭の桁をそのまま繰り上げる方法は、バグなのか意図的なのか本当にわかりません……。1001 元の商品を買ったのに切り上げで 2000 元になったり、199 元の商品が 1000 元になったりするのは、少し大げさだと思います。私が考える端数切り上げの最初の制約は、「切り上げによる割増額が元の金額を超えてはならない」ということです。たとえば 399 元の商品は 400 や 500 に切り上げられますが、1000 元にしてはいけません。1000 - 399 で 601 元の割増になり、商品そのものより高くなってしまうからです!
最初に考えたのは、先頭の桁か 2 桁目から繰り上げる方法でした。たとえば 199 は先頭の桁が 1 なので、2 に繰り上げ、残りの桁を 0 で埋めて 200 にします。この方法に従うと、10599 は 20000 または 11000 になります。見たところ妥当に思えますよね? しかし問題があります! この方法では「切り上げの割増額が元の金額を超える」ことが起こり得ます。消費者から見ると、これはとても奇妙なことです。
その後、物事を複雑に考えすぎていたことに気づきました。端数切り上げは基本的に割り算で実現できます。100 で割って小数点以下を無条件に切り上げ、その後 100 を掛けて元の桁に戻すのです。この方法の利点は、切り上げを百の位までに抑えられるため、極端な結果にならないことです。たとえば 1999 / 100 = 19.99 で、無条件に切り上げると 20 になり、20 * 100 = 2000 となって、基本的な端数切り上げが完成します。この方法では次のような結果が得られます。
- 199 => 200
- 799 => 800
Math.ceil(1999 / 100) * 100この方法なら、切り上げの範囲を百の位に抑えられ、実装も簡単です。ただし、次の数字の場合はどうすればよいでしょうか?
- 15000
- 17500
- 1500
最初は、数字がすでに整数なら、もう切り上げる必要はないと考えていました。しかし、よく考えてみると、消費者は数字がすでに整数だと知っていながら、なぜ切り上げボタンを押すのでしょうか? それは、その数字がまだ「キリが悪い」と感じているということです! あるいは、ただ少し多く払いたいだけかもしれません(笑)。それなら、その願いを叶えてあげるべきです。ここでは、右から見て最初の「0 以外の」数字を探し、その桁を繰り上げる方法を選びました。例:
- 15000 => 16000
もう一つの方法も考え方は似ていますが、数字は最初の 0 以外の数字の一つ前の位に補います。例:
- 15000 => 20000
この方法の JS コードは次のとおりです。処理は 2 段階に分かれており、数字が「100 の倍数の整数ではない」場合は百の位まで切り上げる方法を使い、そうでなければ最初の 0 以外の数字を切り上げる方法を使います。ここでは正規表現で、右から最初の 0 以外の数字の位置を判定しています。
function happy_money(num) {
// すでに百の位まで切り上げ済みの整数の場合
if(Math.floor(num / 100) == num/100) {
// 右から数えて最初の 0 以外の数字の位置
var posi = /[1-9][0]*$/.exec(Number(num).toString())[0].length - 1;
return num + Math.pow(10,posi);
} else {
// 123 -> 200
// 123001 -> 123100
return Math.ceil(num / 100) * 100;
}
}幸運数字に興味があるなら、原理は基本的に、あらかじめ多くの幸運数字を入れた配列を作っておき、消費者の金額に最も近い幸運数字を探して、その数字に移動する、というものです。
私は自分でテストするとき、次の数字を使って、期待どおりの結果になるか、調整が必要かを確かめる習慣があります。
- 99
- 199
- 150
- 1500
- 20000
- 2010100
