re: 10 なら 10、 11 なら 20 になる関数を作りたい

10 なら 10、 11 なら 20 になる関数を作りたいをやってみよう。暇だし。

その1: Math.ceilを使う

まずは先に報告があるMath.ceilを使う方法。

function round(x, a){
	return Math.ceil(x/a)*a;
}

多分、これが正解。

その2: Math.ceilを使わない

xとaが整数であることを前提に、Math.ceilMath.floorに書き換える。

function round(x, a){
	return (Math.floor((x - 1)/a) + 1)*a;
}

その3: Math.floorも使わない (そしてxが0以下の場合、非対応になる)

Math.ceilを使わないんだから、Math.floorも使うのやめてみる。

function round(x, a){
	return (((x - 1) - (x - 1)%a)/a + 1)*a;
}

読みにくいので展開。

function round(x, a){
	return x + a - (x - 1)%a - 1;
}

でも、この書き換えはxが0以下の場合に対応していない。

その4: 0以下の場合にも対応させる

xが0以上なら大丈夫なんだから、xの絶対値 (|x|) より大きいaの倍数、"a*n" をxに足しておいて、後で引けばいい。

function round(x, a){
	return ((x + a*n) + a - ((x + a*n) - 1)%a - 1) - a*n;
}

展開。

function round(x, a){
	return x + a - (x + a*n - 1)%a - 1;
}

nは十分大きい数字なら何だって良いが、桁あふれが起きないようにできるだけ小さい数字を狙ってみる。

a*n > |x|a*n > xなのでn > |x|/an > x/a。aとnは1以上の整数、xは0以下の整数なので、最小のnはMath.floor((x - 1)/a)*-1

xが負の整数であることを前提にMath.floorを展開して、nは(-x + x%a)/a + 1

このとき、xがaの2倍以上の正の整数になると(-x + x%a)/a + 1が負の値になってしまうが、a*((-x + x%a)/a + 1)はxより小さく、また剰余計算の割られる数なので、式の結果には影響しない。

という訳で、nに(-x + x%a)/a + 1を代入する。

function round(x, a){
	return x + a - (x + a*((-x + x%a)/a + 1) - 1)%a - 1;
}

展開。

function round(x, a){
	return x + a - (x%a + a - 1)%a - 1;
}

ところで、これ (x + a - (x%a + a - 1)%a - 1) をみてくれ。コイツをどう思う

全く意味がわかりません。

っていうか、自分で書いておいてなんだけど、何でこの式で答えが得られるのか良くわからないし (おい)。

本気で間違っていても気づかないので、読者各位の検算希望。

今日の結論

素直にMath.ceilを使うのが正解 (ここまでやっておいて、それかよ)。