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