ウォンツテック

そでやまのーと

正の整数A,B,Cの正の和のオーバーフローについて詳しく考える。
まず32bit値の変数を考えたときオーバーフローを起こすのは数値が2^32以上の値になった時であるのでその境界値をMと置く。
するとA+B+Cのオーバーフローは以下のように考えられる

B+Cがオーバーフローをする
もしくは
B+Cがオーバーフローせず かつ A+(B+C)がオーバーフローをする
時
A+B+Cはオーバーフローをする。
簡単のため、オーバーフローをするという述語を「⇒OF」、
オーバーフローしないという述語を「!⇒OF」と置き換えると

「B+C ⇒ OF || ( B+C !⇒ OF && A+(B+C) ⇒ OF)」⇒「A+B+C ⇒ OF」
である事がいえる。
すなわち
「B+C ⇒ OF || ( B+C !⇒ OF && A+(B+C) ⇒ OF)」は「A+B+C⇒OF」の
十分条件になっている。
これからこの逆(必要条件)を証明する。

「A+B+C⇒OF」であるとすると以下のどれかが成り立つ
「A+B ⇒ OF || ( A+B !⇒ OF && (A+B)+C ⇒ OF)」... ①
「B+C ⇒ OF || ( B+C !⇒ OF && A+(B+C) ⇒ OF)」... ②
「C+A ⇒ OF || ( C+A !⇒ OF && (C+A)+B ⇒ OF)」... ③
もし①と②と③が同意であれば求める命題が証明される。

また、対象性より①⇔②ならば同様に②⇔③、①⇔③が成り立つ。
よって①⇔②を証明する。

①であると仮定し、B+CのOF状態で場合分けする
1) B+C ⇒ OF
 これは②の条件を満たしているので真
2) B+C !⇒ OF
 ①の前半の条件の場合 (A+B ⇒ OF)
  B+C < M, A+B >= Mが言えてこれより
  A+(B+C) = (A+B)+C >= M
 すなわち A+(B+C) ⇒ OF
 ①の後半の条件の場合 (A+B !⇒ OF && (A+B)+C ⇒ OF)
  B+C < M, A+B < M, (A+B)+C >= M
 これより
  A+(B+C) = (A+B)+C >= M
 すなわち A+(B+C) ⇒ OF
よって①⇒②は満たされた。

②であると仮定するとこれはA+B+C ⇒ OFである事は明らか

以上より求める命題は証明された。

これから多倍長整数の剰余算以外を実装
以下その一部

template <typename L>
bool BIGINT<L>::isCarryOutPlus(L x, L y, L carry)
{
  L temp = y + carry;
  if (temp < y)
    return true;
  else if (x + temp < x)
    return true;

  return false;
}

template <typename L>
BIGINT<L> BIGINT<L>::operator+(const BIGINT<L>& org)
{
  BIGINT<L> retInt;
  list<L>   temp;

  list<L>   lint2 = org.getLint();
  int       degree2 = org.getDegree();
  bool      sign2   = org.getSign();

  int       longDegree;
  bool      longSign, smallSign;
  typename list<L>::iterator iLong, iLongEnd, iSmall, iSmallEnd;
  if (*this >= org) {
    iLong = lint.begin();
    iSmall = lint2.begin();
    iLongEnd = lint.end();
    iSmallEnd = lint2.end();
    longDegree = degree;
    longSign = sign;
    smallSign = sign2;
  } else {
    iLong = lint2.begin();
    iSmall = lint.begin();
    iLongEnd = lint2.end();
    iSmallEnd = lint.end();
    longDegree = degree2;
    longSign = sign2;
    smallSign = sign;
  }
  L carry = 0;

  if (sign == sign2) {
    L small;
    while (iLong != iLongEnd) {
      if (iSmall == iSmallEnd)
        small = 0;
      else
        small = *iSmall;

      temp.push_back(*iLong + small + carry);

      if (isCarryOutPlus(*iLong, small, carry))
        carry = 1;
      else
        carry = 0;

      iLong++;
      if (iSmall != iSmallEnd)
        iSmall++;
    }

    if (carry != 0) {
      temp.push_back(carry);
      retInt.setDegree(longDegree+1);
    } else
      retInt.setDegree(longDegree);

    retInt.setSign(sign);
  } else { // sign != sign2

    L small;
    while (iLong != iLongEnd) {
      if (iSmall == iSmallEnd)
        small = 0;
      else
        small = *iSmall;

      /* example
       * 1000 - 100 = 900 ... carry = 0
       * 100 - 1000 = 4294966396 ... carry = 1
       * 0 - 4294967295 - 1 = 0 ... carry = 1
       * 0 - 4294967295 = 1 ... carry = 1
       */
      temp.push_back(*iLong - small - carry);

      if (isCarryOutMinus(*iLong, small, carry))
        carry = 1;
      else
        carry = 0;

      iLong++;
      if (iSmall != iSmallEnd)
        iSmall++;
    }

    // Because of checking size between lint and lint2,
    // the BIGINT of return must have the longSign.
    retInt.setSign(longSign);

    if (temp.front() == 0) {
      temp.erase(temp.begin());
      retInt.setDegree(longDegree-1);
    } else {
      retInt.setDegree(longDegree);
    }
  }

  retInt.setLint(temp);
  return retInt;
}