[[C++ BCD class の作成 (7)]]
* 除算の実装 [#i51b963a]
除算は二つ問題がある。
一つは結果に商と剰余という二つの object が生成
されることである。これについては、整数と同様 / 演算で商を、% 演算で
剰余を返すものとする。一度で求まるものを二つに分けるのは不本意で
あるが、仕方無いだろう。
もう一つは零による除算の取り扱いである。C++ の標準仕様には
零による除算の取り扱いは(意図的に)含まれていない。
ハードウェアや OS など、言語仕様の外で捉えられるべきエラーで
あるということのようである。そうは言っても、実装者は何らかの手を
打たないといけないわけで、ここではとりあえず std::runtime_error() でも
投げておくことにする。
** 商の実装 [#y8481083]
まずは / 演算のみを実装しよう。ただし、剰余も同時に求まるので、
補助関数では商を返すと同時に剰余も求まるようにしておこう。
BCD
operator/(const BCD& a, const BCD& b)
{
BCD r;
if(a.m_neg == b.m_neg)
return BCD::absdiv(a, b, r);
BCD c = BCD::absdiv(a, b, r);
c.m_neg = true;
return c;
}
除算の実装には桁移動が多用されるので、10の掛け算、10の割り算
を別途用意しておく(すでに乗算は実装してあるので、必ずしも
必要無いが、、)。
const BCD&
BCD::mul10()
{
if(m_ndig == 0)
return *this;
m_buf.push_back((char)0);
for(int i = 0; i < m_ndig; i++)
m_buf[m_ndig - i] = m_buf[m_ndig - i - 1];
m_buf[0] = 0;
m_ndig = m_ndig + 1;
return *this;
}
const BCD&
BCD::div10()
{
if(m_ndig == 0)
return *this;
for(int i = 1; i < m_ndig; i++)
m_buf[i - 1] = m_buf[i];
m_ndig = m_ndig - 1;
return *this;
}
ここまで実装できると、残りは比較的簡単である。筆算で割り算する通りに
コードを書けばよい。被除数が除数より小さければ 0 で、そうでなければ
被除数が除数より小さくなるまで除数を10倍していって、越えたところで
一桁戻して、除数を引けるだけ引いて、、という具合。
BCD
BCD::absdiv(const BCD& a, const BCD& b, BCD& r)
{
BCD d;
r = a; // residual
r.m_neg = false;
if(b.m_ndig == 0)
throw std::runtime_error("BCD arithmetic error -- divide by zero");
if(abscmp(a, b) < 0)
return d; // returns 0;
BCD c = b;
c.m_neg = false;
int ndig = 0;
while(abscmp(r, c) >= 0)
{
++ndig;
c.mul10();
d.m_buf.push_back((char)0);
}
d.m_ndig = ndig;
while(ndig)
{
--ndig;
c.div10();
while(abscmp(r, c) >= 0)
{
d.m_buf[ndig] += 1;
r = r - c;
}
}
return d;
}
** 剰余の実装 [#u54ce7ab]
ここまでくれば、剰余は符号の処理だけである。
BCD
operator%(const BCD& a, const BCD& b)
{
BCD r;
BCD::absdiv(a, b, r);
if(a.m_neg)
r.m_neg = true;
return r;
}
if 文の部分は
r.m_neg = a.m_neg;
にした方がよいかもしれない。
** テストプログラム [#u2215a58]
零の割り算の時に、std::runtime_error() を throw するので、
以下のテストプログラムは try-catch で囲むのが正当だろう。
以下のテストプログラムは try-catch で囲むのが正当だろうが、
ここではとりあえず無視する。
// bcdtest6.cpp
#include "bcd.h"
int main(int argc, char* argv[])
{
using namespace mylib;
if(argc != 3)
{
std::cout << "Usage: " << argv[0] << " int int" << std::endl;
return 1;
}
BCD bcd1(argv[1]);
BCD bcd2(argv[2]);
std::cout << bcd1 << " + " << bcd2 << " = " << bcd1 + bcd2 << std::endl;
std::cout << bcd1 << " - " << bcd2 << " = " << bcd1 - bcd2 << std::endl;
std::cout << bcd1 << " * " << bcd2 << " = " << bcd1 * bcd2 << std::endl;
std::cout << bcd1 << " / " << bcd2 << " = " << bcd1 / bcd2
<< " ... " << bcd1 % bcd2 << std::endl;
return 0;
}