第3章:テストとデバッグ
はじめに
本章では、CPP Module 02のテストとデバッグ方法を解説します。
---
1. Orthodox Canonical Formのテスト
1.1 コンストラクタの呼び出し順序
void testOCF() {
std::cout << "=== OCF Test ===" << std::endl;
Fixed a; // Default constructor
Fixed b(a); // Copy constructor
Fixed c; // Default constructor
c = a; // Copy assignment operator
std::cout << "=== End OCF Test ===" << std::endl;
}
// デストラクタはスコープ終了時に呼ばれる
1.2 期待される出力パターン
=== OCF Test ===
Default constructor called
Copy constructor called
Default constructor called
Copy assignment operator called
=== End OCF Test ===
Destructor called
Destructor called
Destructor called
1.3 よくある間違い
コピーコンストラクタで代入演算子を呼ぶ問題:
// よくある実装
Fixed::Fixed(const Fixed& other) {
*this = other; // 代入演算子を呼ぶ
}
// この場合、出力は:
// Copy constructor called
// Copy assignment operator called
この実装自体は動作しますが、効率的ではありません。
---
2. 固定小数点数の精度テスト
2.1 変換精度の確認
void testPrecision() {
std::cout << "=== Precision Test ===" << std::endl;
// 整数変換
Fixed i1(0);
Fixed i2(42);
Fixed i3(-42);
std::cout << "int 0: " << i1 << std::endl;
std::cout << "int 42: " << i2 << std::endl;
std::cout << "int -42: " << i3 << std::endl;
// 浮動小数点変換
Fixed f1(0.0f);
Fixed f2(42.42f);
Fixed f3(-42.42f);
Fixed f4(0.5f);
Fixed f5(0.25f);
std::cout << "float 0.0: " << f1 << std::endl;
std::cout << "float 42.42: " << f2 << std::endl;
std::cout << "float -42.42: " << f3 << std::endl;
std::cout << "float 0.5: " << f4 << std::endl;
std::cout << "float 0.25: " << f5 << std::endl;
}
2.2 精度の限界
8ビットの小数部では、最小単位は 1/256 = 0.00390625 です。
void testMinUnit() {
Fixed a;
Fixed b;
a.setRawBits(1); // 最小単位
std::cout << "Minimum unit: " << a << std::endl;
b.setRawBits(256); // = 1.0
std::cout << "256 raw bits: " << b << std::endl;
}
2.3 toInt()の丸め
void testToInt() {
Fixed f1(42.9f);
Fixed f2(42.1f);
Fixed f3(-42.9f);
std::cout << "42.9 -> " << f1.toInt() << std::endl; // 42
std::cout << "42.1 -> " << f2.toInt() << std::endl; // 42
std::cout << "-42.9 -> " << f3.toInt() << std::endl; // -42
}
---
3. 演算子のテスト
3.1 比較演算子
void testComparison() {
Fixed a(10);
Fixed b(20);
Fixed c(10);
std::cout << "=== Comparison Test ===" << std::endl;
std::cout << "a(10) > b(20): " << (a > b) << std::endl; // 0
std::cout << "a(10) < b(20): " << (a < b) << std::endl; // 1
std::cout << "a(10) >= c(10): " << (a >= c) << std::endl; // 1
std::cout << "a(10) == c(10): " << (a == c) << std::endl; // 1
std::cout << "a(10) != b(20): " << (a != b) << std::endl; // 1
}
3.2 算術演算子
void testArithmetic() {
Fixed a(10);
Fixed b(3);
std::cout << "=== Arithmetic Test ===" << std::endl;
std::cout << "10 + 3 = " << (a + b) << std::endl; // 13
std::cout << "10 - 3 = " << (a - b) << std::endl; // 7
std::cout << "10 * 3 = " << (a * b) << std::endl; // 30
std::cout << "10 / 3 = " << (a / b) << std::endl; // 3.33...
}
3.3 インクリメント/デクリメント
void testIncrement() {
Fixed a;
std::cout << "=== Increment Test ===" << std::endl;
std::cout << "Initial: " << a << std::endl; // 0
std::cout << "++a: " << ++a << std::endl; // 0.00390625
std::cout << "After ++a: " << a << std::endl; // 0.00390625
std::cout << "a++: " << a++ << std::endl; // 0.00390625
std::cout << "After a++: " << a << std::endl; // 0.0078125
}
3.4 min/max
void testMinMax() {
Fixed a(5.5f);
Fixed b(10.5f);
std::cout << "=== Min/Max Test ===" << std::endl;
std::cout << "min(5.5, 10.5): " << Fixed::min(a, b) << std::endl; // 5.5
std::cout << "max(5.5, 10.5): " << Fixed::max(a, b) << std::endl; // 10.5
// const版のテスト
Fixed const c(3.3f);
Fixed const d(7.7f);
std::cout << "min(const 3.3, const 7.7): " << Fixed::min(c, d) << std::endl;
}
---
4. BSPのテスト
4.1 基本テストケース
void testBSP() {
// 三角形: (0,0), (10,0), (5,10)
Point a(0.0f, 0.0f);
Point b(10.0f, 0.0f);
Point c(5.0f, 10.0f);
std::cout << "=== BSP Test ===" << std::endl;
// 明らかに内部
Point p1(5.0f, 3.0f);
std::cout << "(5, 3) inside: " << bsp(a, b, c, p1) << std::endl; // true
// 明らかに外部
Point p2(0.0f, 10.0f);
std::cout << "(0, 10) outside: " << bsp(a, b, c, p2) << std::endl; // false
// 辺上
Point p3(5.0f, 0.0f);
std::cout << "(5, 0) on edge: " << bsp(a, b, c, p3) << std::endl; // false
// 頂点上
Point p4(0.0f, 0.0f);
std::cout << "(0, 0) on vertex: " << bsp(a, b, c, p4) << std::endl; // false
}
4.2 エッジケース
void testBSPEdgeCases() {
std::cout << "=== BSP Edge Cases ===" << std::endl;
// 非常に小さい三角形
Point a1(0.0f, 0.0f);
Point b1(0.1f, 0.0f);
Point c1(0.05f, 0.1f);
Point p1(0.05f, 0.05f);
std::cout << "Tiny triangle: " << bsp(a1, b1, c1, p1) << std::endl;
// 点が三角形の境界に非常に近い
Point a2(0.0f, 0.0f);
Point b2(10.0f, 0.0f);
Point c2(5.0f, 10.0f);
Point p2(0.01f, 0.01f);
std::cout << "Near boundary: " << bsp(a2, b2, c2, p2) << std::endl;
}
4.3 視覚的なデバッグ
void visualDebug() {
Point a(0.0f, 0.0f);
Point b(10.0f, 0.0f);
Point c(5.0f, 10.0f);
// 10x10のグリッドでテスト
std::cout << "Visual grid (. = outside, * = inside):" << std::endl;
for (int y = 10; y >= 0; y--) {
for (int x = 0; x <= 10; x++) {
Point p(static_cast<float>(x), static_cast<float>(y));
if (bsp(a, b, c, p))
std::cout << "* ";
else
std::cout << ". ";
}
std::cout << std::endl;
}
}
---
5. よくある間違いと対処法
5.1 乗算のオーバーフロー
// 問題: 大きな数の乗算でオーバーフロー
Fixed a(1000);
Fixed b(1000);
Fixed c = a * b; // オーバーフローの可能性
// 内部計算:
// 1000 << 8 = 256000
// 256000 * 256000 = 65536000000 (intの範囲を超える)
対処法: long longを使用するか、値の範囲を制限する。
5.2 除算のゼロチェック
// 問題: ゼロ除算
Fixed a(10);
Fixed b(0);
Fixed c = a / b; // 未定義動作
// 対処法: チェックを追加
Fixed Fixed::operator/(const Fixed& rhs) const {
if (rhs.getRawBits() == 0) {
std::cerr << "Error: Division by zero" << std::endl;
return Fixed(0);
}
Fixed result;
result.setRawBits((this->_rawBits << _fractionalBits) / rhs._rawBits);
return result;
}
5.3 自己代入のチェック
// 自己代入のチェックを忘れると問題になる場合がある
Fixed& Fixed::operator=(const Fixed& other) {
if (this != &other) { // 重要!
this->_rawBits = other._rawBits;
}
return *this;
}
5.4 constメンバの代入演算子
// Pointクラスでconstメンバがある場合
class Point {
Fixed const _x; // const!
Fixed const _y; // const!
// 代入演算子は何もできない
Point& operator=(const Point& other) {
(void)other;
return *this;
}
};
---
6. 提出前チェックリスト
ex00
- [ ] デフォルトコンストラクタがある
- [ ] コピーコンストラクタがある
- [ ] コピー代入演算子がある
- [ ] デストラクタがある
- [ ] 出力メッセージが正確
- [ ] int/floatコンストラクタがある
- [ ] toFloat()/toInt()が正しく動作
- [ ]
<<演算子がオーバーロードされている - [ ] roundf()を使用している
- [ ] 6つの比較演算子がある
- [ ] 4つの算術演算子がある
- [ ] 前置/後置インクリメント/デクリメントがある
- [ ] min/max静的関数がある(const版も)
- [ ] Pointクラスがある
- [ ] bsp関数が正しく動作
- [ ] 辺上・頂点上の点はfalseを返す
- OCF: 4つの特殊メンバ関数の呼び出し順序
- 精度: 固定小数点数の変換精度
- 演算子: すべての演算子の動作
- BSP: 幾何学的判定の正確性
ex01
ex02
ex03
---
まとめ
CPP Module 02のテストでは以下を確認: