固定小数点数クラス

OpenGL ES を使う上ですごく必要になってきたので作ってみた。

// 固定小数点数クラス(Q16)
struct fixed
{
private:
    int32 v_;

public:
    fixed() : v_(0) { }
    fixed(const fixed& f) : v_(f.v_) { }
    // implicit で大丈夫だよね?オーバーフローしないように注意すること。
    fixed(int32 v) : v_(v << 16) { }
    // n/m を格納
    fixed(int32 n, int32 m) : v_((int32)(((int64)n << 16) / m)) { }

    int32 get() const { return v_; }

    fixed& operator=(const fixed& f)
    {
        v_ = f.v_;
        return *this;
    }
    fixed operator+() const
    {
        return *this;
    }
    fixed operator-() const
    {
        fixed v;
        v.v_ = -v_;
        return v;
    }
    fixed& operator+=(const fixed& f)
    {
        v_ += f.v_;
        return *this;
    }
    fixed& operator-=(const fixed& f)
    {
        v_ -= f.v_;
        return *this;
    }
    fixed& operator*=(const fixed& f)
    {
        v_ = (v_ >> 8) * (f.v_ >> 8);
        return *this;
    }
    fixed& operator/=(const fixed& f)
    {
        v_ = (((int64)v_ << 32) / f.v_) >> 16;
        return *this;
    }
    fixed& operator>>=(int v)
    {
        v_ >>= v;
        return *this;
    }
    fixed& operator<<=(int v)
    {
        v_ <<= v;
        return *this;
    }
};

inline fixed operator+(const fixed& a, const fixed& b)
{
    fixed v(a);
    return v += b;
}
inline fixed operator-(const fixed& a, const fixed& b)
{
    fixed v(a);
    return v -= b;
}
inline fixed operator*(const fixed& a, const fixed& b)
{
    fixed v(a);
    return v *= b;
}
inline fixed operator/(const fixed& a, const fixed& b)
{
    fixed v(a);
    return v /= b;
}
inline fixed operator>>(const fixed& a, int b)
{
    fixed v(a);
    return v >>= b;
}
inline fixed operator<<(const fixed& a, int b)
{
    fixed v(a);
    return v <<= b;
}
inline bool operator==(const fixed& a, const fixed& b)
{
    return a.get() == b.get();
}
inline bool operator!=(const fixed& a, const fixed& b)
{
    return !(a == b);
}
inline bool operator<(const fixed& a, const fixed& b)
{
    return a.get() < b.get();
}
inline bool operator>(const fixed& a, const fixed& b)
{
    return b < a;
}
inline bool operator<=(const fixed& a, const fixed& b)
{
    return !(b < a);
}
inline bool operator>=(const fixed& a, const fixed& b)
{
    return !(a < b);
}

まあ、普通の実装かな?除算が重そうだけど。


でもこのクラス、コンストラクタが implicit なので、

class texture
{
public:
    uint32 width() const { ... }
    uint32 height() const { ... }
    ...
};

draw(const texture& t, const fixed& x, const fixed& y);

こんなのがあったときに

texture t;
fixed x,y;
draw(t, x - t.width() / 2, y - t.height() / 2);

とか書けて結構便利だったりする。
ただ、時々意図しない変換が行われちゃったりするかもしれない(GLfixed と計算しようとした場合とか)のでその辺は注意する必要があったり。
GLfixed をセットしたかったら、

GLfixed glf;
fixed f(glf, 65536);

とか書く必要があるんだけど、やっぱり raw データをセットするコンストラクタがいるかなぁ……。

struct fixed
{
private:
    int32 v_;

public:
    struct raw_tag { };
    fixed(int32 v, raw_tag) : v_(v) { }
    ...
};
GLfixed glf;
fixed f(glf, fixed::raw_tag());

みたいな感じで。