Skip to content

Instantly share code, notes, and snippets.

@jaburns
Created August 23, 2017 16:31
Show Gist options
  • Save jaburns/dc02091c64e9b5cdaff6c1e5df3ad93c to your computer and use it in GitHub Desktop.
Save jaburns/dc02091c64e9b5cdaff6c1e5df3ad93c to your computer and use it in GitHub Desktop.
C++ fixed-point 32 bit type
#include "fixed32.hpp"
#include <cmath>
static const int32_t DECIMAL_BITS = 16;
const fixed32 fixed32::ZERO = fixed32(0);
const fixed32 fixed32::ONE = fixed32(1);
const fixed32 fixed32::MINUS_ONE = fixed32(-1);
const fixed32 fixed32::TWO = fixed32(2);
const fixed32 fixed32::TWO_PI = fixed32::from_raw_int(411774); // fixed32::from_float(6.283185307179586).to_raw_int();
const fixed32 fixed32::PI = TWO_PI / TWO;
const fixed32 fixed32::PI_OVER_TWO = PI / TWO;
fixed32::fixed32()
{
m_int = 0;
}
fixed32::fixed32(int16_t init)
{
m_int = init << DECIMAL_BITS;
}
fixed32 fixed32::from_raw_int(int32_t raw)
{
fixed32 ret;
ret.m_int = raw;
return ret;
}
fixed32 fixed32::from_fraction(int32_t numerator, int32_t denominator)
{
return from_raw_int(static_cast<int16_t>((numerator << DECIMAL_BITS) / denominator));
}
int32_t fixed32::to_raw_int() const
{
return m_int;
}
fixed32 fixed32::from_float(float x)
{
return fixed32::from_raw_int(static_cast<int32_t>(x * 65536.0f));
}
float fixed32::to_float() const
{
return static_cast<float>(m_int) / 65536.0f;
}
glm::vec2 fixed32::to_float(vec2 v)
{
return { v.x.to_float(), v.y.to_float() };
}
glm::vec3 fixed32::to_float(vec3 v)
{
return { v.x.to_float(), v.y.to_float(), v.z.to_float() };
}
fixed32::vec2 fixed32::from_float(glm::vec2 v)
{
return { from_float(v.x), from_float(v.y) };
}
fixed32::vec3 fixed32::from_float(glm::vec3 v)
{
return { from_float(v.x), from_float(v.y), from_float(v.z) };
}
fixed32& fixed32::operator +=(fixed32 rhs)
{
m_int += rhs.m_int;
return *this;
}
fixed32& fixed32::operator -=(fixed32 rhs)
{
m_int -= rhs.m_int;
return *this;
}
fixed32& fixed32::operator *=(fixed32 rhs)
{
const int64_t lhs64 = m_int;
const int64_t rhs64 = rhs.m_int;
const int64_t result = lhs64 * rhs64;
m_int = static_cast<int32_t>(result >> DECIMAL_BITS);
return *this;
}
fixed32& fixed32::operator /=(fixed32 rhs)
{
const int64_t lhs64 = m_int;
const int64_t rhs64 = rhs.m_int;
const int64_t result = (lhs64 << DECIMAL_BITS) / rhs64;
m_int = static_cast<int32_t>(result);
return *this;
}
fixed32& fixed32::operator %=(fixed32 rhs)
{
m_int %= rhs.m_int;
return *this;
}
fixed32 fixed32::operator +(fixed32 rhs) const { fixed32 ret = *this; ret += rhs; return ret; }
fixed32 fixed32::operator -(fixed32 rhs) const { fixed32 ret = *this; ret -= rhs; return ret; }
fixed32 fixed32::operator *(fixed32 rhs) const { fixed32 ret = *this; ret *= rhs; return ret; }
fixed32 fixed32::operator /(fixed32 rhs) const { fixed32 ret = *this; ret /= rhs; return ret; }
fixed32 fixed32::operator %(fixed32 rhs) const { fixed32 ret = *this; ret %= rhs; return ret; }
fixed32 fixed32::operator -() const { return from_raw_int(-m_int); }
bool fixed32::operator < (fixed32 rhs) const { return m_int < rhs.m_int; }
bool fixed32::operator > (fixed32 rhs) const { return m_int > rhs.m_int; }
bool fixed32::operator <=(fixed32 rhs) const { return m_int <= rhs.m_int; }
bool fixed32::operator >=(fixed32 rhs) const { return m_int >= rhs.m_int; }
bool fixed32::operator ==(fixed32 rhs) const { return m_int == rhs.m_int; }
bool fixed32::operator !=(fixed32 rhs) const { return m_int != rhs.m_int; }
fixed32 fixed32::abs() const
{
return m_int < 0 ? -(*this) : *this;
}
fixed32 fixed32::sqrt() const
{
# define INT_ABS(x) ((x) < 0 ? -(x) : (x))
fixed32 guess = *this / TWO;
int32_t int_diff;
do {
fixed32 new_guess = (guess + *this / guess) / TWO;
int_diff = new_guess.m_int - guess.m_int;
guess.m_int = new_guess.m_int;
} while(INT_ABS(int_diff) > 16); // within 16 / 65536 = 0.00024 of previous guess
return guess;
# undef INT_ABS
}
fixed32 fixed32::sin() const
{
return (*this - PI_OVER_TWO).cos();
}
static const fixed32 FOUR_FACTORIAL = fixed32(24);
static const fixed32 SIX_FACTORIAL = fixed32(720);
fixed32 fixed32::cos() const
{
auto x = *this;
auto negate = false;
x %= TWO_PI;
if (x < -PI_OVER_TWO) {
x += PI;
negate = true;
} else if (x > PI_OVER_TWO) {
x -= PI;
negate = true;
}
const auto x2 = x * x;
const auto x4 = x2 * x2;
const auto x6 = x4 * x2;
const auto ret = ONE - x2 / TWO + x4 / FOUR_FACTORIAL - x6 / SIX_FACTORIAL;
return negate ? -ret : ret;
}
fixed32 fixed32::pow(int32_t p) const
{
bool negative_power = false;
if (p < 0) {
negative_power = true;
p = -p;
}
if (p == 0) return 1;
if (p == 1) return *this;
fixed32 result = *this;
while (--p > 0) result *= *this;
return negative_power ? fixed32::ONE / result : result;
}
fixed32 fixed32::max(fixed32 a, fixed32 b)
{
return a.m_int > b.m_int ? a : b;
}
fixed32 fixed32::length(const fixed32::vec2& v)
{
return (v.x * v.x + v.y * v.y).sqrt();
}
fixed32::vec2 fixed32::normalize(const fixed32::vec2& v)
{
const auto len = length(v);
return { v.x / len, v.y / len };
}
fixed32::vec2 fixed32::rotate90(const fixed32::vec2& v)
{
return { -v.y, v.x };
}
fixed32 fixed32::dot(const fixed32::vec2& a, const fixed32::vec2& b)
{
return a.x*b.x + a.y*b.y;
}
fixed32 fixed32::cross(const fixed32::vec2& a, const fixed32::vec2& b)
{
return a.x*b.y - a.y*b.x;
}
fixed32::vec2 fixed32::reflect(const fixed32::vec2& v, const fixed32::vec2& unit_normal, fixed32 normal_scale, fixed32 tangent_scale)
{
const auto unit_tangent = rotate90(unit_normal);
const auto norm_component = -normal_scale * dot(v, unit_normal);
const auto tang_component = tangent_scale * dot(v, unit_tangent);
return unit_normal * norm_component + unit_tangent * tang_component;
}
#pragma once
#include <cstdint>
#include <glm/vec2.hpp>
#include <glm/vec3.hpp>
class fixed32
{
int32_t m_int = 0;
public:
static const fixed32 ZERO;
static const fixed32 ONE;
static const fixed32 MINUS_ONE;
static const fixed32 TWO;
static const fixed32 TWO_PI;
static const fixed32 PI;
static const fixed32 PI_OVER_TWO;
fixed32();
fixed32(int16_t init);
static fixed32 from_raw_int(int32_t raw);
static fixed32 from_fraction(int32_t numerator, int32_t denominator);
static fixed32 from_float(float x);
int32_t to_raw_int() const;
float to_float() const;
typedef glm::tvec2<fixed32> vec2;
typedef glm::tvec3<fixed32> vec3;
static glm::vec2 to_float(vec2 v);
static glm::vec3 to_float(vec3 v);
static vec2 from_float(glm::vec2 v);
static vec3 from_float(glm::vec3 v);
fixed32& operator +=(fixed32 rhs);
fixed32& operator -=(fixed32 rhs);
fixed32& operator *=(fixed32 rhs);
fixed32& operator /=(fixed32 rhs);
fixed32& operator %=(fixed32 rhs);
fixed32 operator +(fixed32 rhs) const;
fixed32 operator -(fixed32 rhs) const;
fixed32 operator *(fixed32 rhs) const;
fixed32 operator /(fixed32 rhs) const;
fixed32 operator %(fixed32 rhs) const;
fixed32 operator -() const;
bool operator < (fixed32 rhs) const;
bool operator > (fixed32 rhs) const;
bool operator <=(fixed32 rhs) const;
bool operator >=(fixed32 rhs) const;
bool operator ==(fixed32 rhs) const;
bool operator !=(fixed32 rhs) const;
fixed32 abs() const;
fixed32 sqrt() const;
fixed32 sin() const;
fixed32 cos() const;
fixed32 pow(int32_t p) const;
static fixed32 max(fixed32 a, fixed32 b);
static fixed32 length(const vec2& v);
static vec2 normalize(const vec2& v);
static vec2 rotate90(const vec2& v);
static fixed32 dot(const fixed32::vec2& a, const fixed32::vec2& b);
static fixed32 cross(const fixed32::vec2& a, const fixed32::vec2& b);
static vec2 reflect(const vec2& v, const vec2& unit_normal, fixed32 normal_scale, fixed32 tangent_scale);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment