1.10 整数除算(商+剰余, 標準 C ライブラリの汎用版)

標準 C ライブラリには、div または ldiv という、整数除算における商と剰余を対にした構造体を返す関数があります。C99 では、これらに加えて、lldiv という long long 型版の関数と、imaxdiv という intmax_t 型版の関数が用意されています。

では、C++ではどうかというと、div および ldiv がそのまま提供されているほか、long 型版の div が多重定義されています。これにより、実引数が int 型か long 型かを気にすることなく、単に std::div(a, b).quot または std::div(a, b).rem と記述すれば、適切な関数が呼び出されます。

しかし、関数名は多重定義されていますが、返却型は依然として div_t および ldiv_t ですので、テンプレートの中で使うには不向きです。そこで、結果を格納する構造体もテンプレートにしてみることにしました。

template<class T>
struct div_t
{
  T quot;
  T rem;
};

template<class T, bool Integer>
struct div_helper
{
  static div_t<T> op(T numer, T denom)
  {
    div_t<T> result;
    result.quot = numer / denom;
    result.rem  = numer % denom;
    return result;
  }
};

template<class T>
struct div_helper<T, false>
{
  static div_t<T> op(T numer, T denom)
  {
    div_t<T> result;
    result.quot = numer / denom;
    std::modf(result.quot, &result.quot);
    result.rem = numer - result.quot * denom;
    return result;
  }
};

template<class T>
div_t<T> div(T numer, T denom)
{
  return div_helper<T, std::numeric_limits<T>::is_integer>::op(numer, denom);
}

div_tdiv といった識別子は、本来 std 名前空間の中で宣言されるべきものですから、名前の衝突は起こらないはずなのですが、現実にはそうなっていない実装も多く、名前が衝突してしまいます。使用される場合は、適当な名前空間の中に入れるか、名前を変更してみてください。

なお、符号付き整数型で、一方が正、他方が負の場合の結果は処理系に依存するわけですが、C99 では結果が固定されたことからもわかるように、すでに div 関数と同等の振る舞いを処理系しか実在しないものと考えてもよさそうです。というわけで、今回は、シンプルに / と % を使って実装してみました。このように、連続してオペランドを用いて除算と剰余算を行うと、うまく最適化されることも多いようなので、あえてこのようにしています。

また、今回のオマケ的として、符号小数点数の商と剰余にも対応するようにしてみました。その他、必要に応じて、div 関数を多重定義すれば、BCD 演算クラス用のものや、固定小数点数クラス用のものも、同じシンタックスで使えるようになるはずです。


元ネタ
このエントリーを含むはてなブックマーク