[[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;
 }

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS