大阪市中央区 システムソフトウェア開発会社

営業時間:平日09:15〜18:15
MENU

C++でC風ライブラリを作る(商と剰余編2)

著者:高木信尚
公開日:2019/02/17
最終更新日:2019/02/17
カテゴリー:技術情報

高木です。こんばんは。

前回は、商と剰余を求めるdiv関数のうち、2つの引数、numerdenomが同じ型のものを作成しました。
しかし、これだけでは不便です。
実引数の型が異なれば、多重定義が解決できずにコンパイルエラーになってしまうからです。

今回は、2つの実引数の型が異なる場合にも使うことができるudiv関数を設計&実装してみたいと思います。
udiv関数はその名の通り、商と剰余を符合無し整数型で返します。
しかし、2つの実引数を単純に符合無し整数型にそろえてから演算を行うだけでは面白くありません。
結果の符合についても何らかの形で返すことにします。

ところで、2つの実引数が異なる場合でも、両方が符合付き整数型または符合無し整数型の場合は前回のdiv関数を使えますので、今回はあくまでも符合の有無が異なる引数が混在する場合について考えます。
もちろん、udiv関数に同じ型の実引数を渡せないようでは不便なので、こちらも合わせて考えていくことにしましょう。

intおよびunsigned int型の組み合わせだけを考えても次の4種類があることになります。

  • intint
  • unsigned intunsigned int
  • intunsigned int
  • unsigned intint

サイズが異なる型、すなわちlonglong longについても同じことがいえます。

次に返却値の型を考えることにしましょう。

結果を符合無し整数型で返すことは前述の通りですが、引数に符合付き整数型が混在した場合、結果にも符合が必要になることは間違いありません。
単純に、

template <typename T>
struct udiv_t
{
  T quot;
  T rem;
  bool quot_sign;
  bool rem_sign;
};

のようにすることもできますが、これでは符合の存在を忘れるミスを誘発してしまいます。
いろいろ考え方はあると思いますが、今回は次のように、quotおよびremの型を工夫することで極力ミスを軽減することにしました。

namespace cloverfield
{
  template <typename T>
  struct udiv_t
  {
    struct
    {
      T abs;
      bool sign;
    } quot, rem;
  };  
}

ここまで方針が決まれば、あとは機械的な作業になります。

まずは、共通のヘルパー関数を作ることにしましょう。
被除数と除数の絶対値と符合を受け取って結果を求める関数です。
型が違ってもやることは同じですのでテンプレートにします。

namespace cloverfield
{
  namespace detail
  {
    template <typename T>
    constexpr udiv_t<T> udiv_helper(T numer, bool numer_sign, T denom, bool denom_sign)
    {
      return {
        {
          denom == 0 ? (throw std::invalid_argument("cloverfield::udiv"), 0) : numer / denom,
          numer_sign ^ denom_sign
        },
        {
          numer % denom,
          numer_sign
        }
      };
    }
  }
}

あとは、このヘルパー関数を呼び出すudiv関数を型ごとに定義すれば完成です。

namespace cloverfield
{
  constexpr udiv_t<unsigned int> udiv(unsigned int numer, unsigned int denom)
  {
    return detail::udiv_helper(numer, false, denom, false);
  }

  constexpr udiv_t<unsigned int> udiv(int numer, int denom)
  {
    return detail::udiv_helper(uabs(numer), numer < 0, uabs(denom), denom < 0);
  }

  constexpr udiv_t<unsigned int> udiv(int numer, unsigned int denom)
  {
    return detail::udiv_helper(uabs(numer), numer < 0, denom, false);
  }

  constexpr udiv_t<unsigned int> udiv(unsigned int numer, int denom)
  {
    return detail::udiv_helper(numer, false, uabs(denom), denom < 0);
  }
}

今回は符合付き整数型どうしであってもオーバーフローが発生することはありませんので、エラーチェックはゼロ除算だけにしています。

    上に戻る