32ビット固定小数3次元ベクトルクラス
#ifndef VECTOR3FIX_H #define VECTOR3FIX_H #include <iostream> #include <math.h> class Vector3fix { public: int x; int y; int z; Vector3fix() : x(0), y(0), z(0) {} Vector3fix(int x, int y, int z) : x(x << 16), y(y << 16), z(z << 16) {} Vector3fix(float x, float y, float z) : x(toFix(x)), y(toFix(y)), z(toFix(z)) {} Vector3fix(const Vector3fix& start, const Vector3fix& end) { this->x = end.x - start.x; this->y = end.y - start.y; this->z = end.z - start.z; } const void set(int x, int y, int z) { this->x = x << 16; this->y = y << 16; this->z = z << 16; } const void add(const Vector3fix& v) { this->x += v.x; this->y += v.y; this->z += v.z; } const void sub(const Vector3fix& v) { this->x -= v.x; this->y -= v.y; this->z -= v.z; } const void mul(int n) { this->x *= n; this->y *= n; this->z *= n; } const void mul(float n) { this->x = static_cast<int>(this->x * n); this->y = static_cast<int>(this->y * n); this->z = static_cast<int>(this->z * n); } const void normalize() { float floatX = toFloat(this->x); float floatY = toFloat(this->y); float floatZ = toFloat(this->z); float l = 1.0f / static_cast<float>(sqrt( floatX * floatX + floatY * floatY + floatZ * floatZ)); this->x = toFix(floatX * l); this->y = toFix(floatY * l); this->z = toFix(floatZ * l); } const void invert() { this->x *= -1; this->y *= -1; this->z *= -1; } const float dotProduct(const Vector3fix& v) const { float v1x = toFloat(this->x); float v1y = toFloat(this->y); float v1z = toFloat(this->z); float v2x = toFloat(v.x); float v2y = toFloat(v.y); float v2z = toFloat(v.z); return v1x * v2x + v1y * v2y + v1z * v2z; } const void crossProduct(const Vector3fix& v1, const Vector3fix& v2) { float v1x = toFloat(v1.x); float v1y = toFloat(v1.y); float v1z = toFloat(v1.z); float v2x = toFloat(v2.x); float v2y = toFloat(v2.y); float v2z = toFloat(v2.z); this->x = toFix(v1y * v2z - v1z * v2y); this->y = toFix(v1z * v2x - v1x * v2z); this->z = toFix(v1x * v2y - v1y * v2x); } // メンバを浮動小数で返す const float getFloatX() const { return toFloat(this->x); } const float getFloatY() const { return toFloat(this->y); } const float getFloatZ() const { return toFloat(this->z); } friend std::ostream& operator << (std::ostream &stream, const Vector3fix& v); private: const float toFloat(int ival) const { return static_cast<float>(ival) / 0x10000; } const int toFix(float fval) const { return static_cast<int>(fval * 0x10000); } }; ostream& operator << (std::ostream &os, const Vector3fix& v) { os << "(" << v.getFloatX() << ", " << v.getFloatY() << ", " << v.getFloatZ() << ")"; return os; } #endif // for Unit-Test int main (void) { Vector3fix vec1(1, 0, 0); Vector3fix vec2(1, 1, 1); Vector3fix vec3(vec1, vec2); cout << vec1 << ", " << vec2 << ", " << vec3 << endl; vec3.crossProduct(vec1, vec2); cout << vec3 << endl; vec1.add(Vector3fix(33, 44, 55)); cout << vec1 << endl; vec1.sub(Vector3fix(11, 22, 33)); cout << vec1 << endl; vec3.set(1, 1, 1); cout << vec1.dotProduct(vec3) << endl; vec1.mul(100); vec1.invert(); cout << vec1 << endl; vec1.normalize(); cout << vec1 << endl; return 0; }
Output:
(1, 0, 0), (1, 1, 1), (0, 1, 1) (0, -1, 1) (34, 44, 55) (23, 22, 22) 67 (-2300, -2200, -2200) (-0.594452, -0.568604, -0.568604)
考察
固定小数同士を掛け算させると簡単にオーバーフローしてしまうので、
内部で掛け算を要するメソッドは、計算前にメンバを必ず浮動小数に戻しておく必要がある。
高速化のために固定小数クラスを実装してみたが、この処理のせいでかえって遅くなるかも。
コードサイズも大きくなりそうだし、有用性については要検証・・・。
追記(09/10/23)
long long 型を使えば、浮動小数に戻さずともオーバーフローのない整数計算ができる。
(どちらが早いかは、ベンチマークを取ってみないと分からない。)
const void crossProduct(const Vector3fix& v1, const Vector3fix& v2) { long long v1x = v1.x; long long v1y = v1.y; long long v1z = v1.z; long long v2x = v2.x; long long v2y = v2.y; long long v2z = v2.z; this->x = (v1y * v2z - v1z * v2y) >> 16; this->y = (v1z * v2x - v1x * v2z) >> 16; this->z = (v1x * v2y - v1y * v2x) >> 16; }