1
0
forked from 0ad/0ad
0ad/source/maths/Fixed.h

166 lines
4.3 KiB
C
Raw Normal View History

/* Copyright (C) 2010 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_FIXED
#define INCLUDED_FIXED
#include "lib/types.h"
#include "maths/Sqrt.h"
template <typename T>
inline T round_away_from_zero(float value)
{
return (T)(value >= 0 ? value + 0.5f : value - 0.5f);
}
/**
* A simple fixed-point number class, with no fancy features
* like overflow checking or anything. (It has very few basic
* features too, and needs to be substantially improved before
* it'll be of much use.)
*
* Use CFixed_23_8 rather than using this class directly.
*/
template<typename T, T max_t, int total_bits, int int_bits, int fract_bits_, int fract_pow2>
class CFixed
{
private:
T value;
explicit CFixed(T v) : value(v) { }
public:
enum { fract_bits = fract_bits_ };
CFixed() : value(0) { }
T GetInternalValue() const { return value; }
void SetInternalValue(T n) { value = n; }
// Conversion to/from primitive types:
static CFixed FromInt(int n)
{
return CFixed(n << fract_bits);
}
static CFixed FromFloat(float n)
{
if (!isfinite(n))
return CFixed(0);
float scaled = n * fract_pow2;
return CFixed(round_away_from_zero<T>(scaled));
}
static CFixed FromDouble(double n)
{
if (!isfinite(n))
return CFixed(0);
double scaled = n * fract_pow2;
return CFixed(round_away_from_zero<T>(scaled));
}
float ToFloat() const
{
return value / (float)fract_pow2;
}
double ToDouble() const
{
return value / (double)fract_pow2;
}
int ToInt_RoundToZero() const
{
if (value > 0)
return value >> fract_bits;
else
return (value + fract_pow2 - 1) >> fract_bits;
}
/// Returns true if the number is precisely 0.
bool IsZero() const { return value == 0; }
/// Equality.
bool operator==(CFixed n) const { return (value == n.value); }
/// Numeric comparison.
bool operator<=(CFixed n) const { return (value <= n.value); }
/// Numeric comparison.
bool operator<(CFixed n) const { return (value < n.value); }
/// Numeric comparison.
bool operator>=(CFixed n) const { return (value >= n.value); }
/// Numeric comparison.
bool operator>(CFixed n) const { return (value > n.value); }
// Basic arithmetic:
/// Add a CFixed. Might overflow.
CFixed operator+(CFixed n) const { return CFixed(value + n.value); }
/// Subtract a CFixed. Might overflow.
CFixed operator-(CFixed n) const { return CFixed(value - n.value); }
/// Negate a CFixed.
CFixed operator-() const { return CFixed(-value); }
/// Divide by a CFixed. Must not have n.IsZero(). Might overflow.
CFixed operator/(CFixed n) const
{
i64 t = (i64)value << fract_bits;
return CFixed((T)(t / (i64)n.value));
}
/// Multiply by an integer. Might overflow.
CFixed operator*(int n) const { return CFixed(value * n); }
/// Divide by an integer. Must not have n == 0. Cannot overflow.
CFixed operator/(int n) const { return CFixed(value / n); }
CFixed Absolute() const { return CFixed(abs(value)); }
/**
* Multiply by a CFixed. Likely to overflow if both numbers are large,
* so we use an ugly name instead of operator* to make it obvious.
*/
CFixed Multiply(CFixed n) const
{
i64 t = (i64)value * (i64)n.value;
return CFixed((T)(t >> fract_bits));
}
CFixed Sqrt() const
{
if (value <= 0)
return CFixed(0);
u32 s = isqrt64(value);
return CFixed((u64)s << (fract_bits / 2));
}
};
/**
* A fixed-point number class with 1-bit sign, 23-bit integral part, 8-bit fractional part.
*/
typedef CFixed<i32, (i32)0x7fffffff, 32, 23, 8, 256> CFixed_23_8;
/**
* Inaccurate approximation of atan2 over fixed-point numbers.
* Maximum error is almost 0.08 radians (4.5 degrees).
*/
CFixed_23_8 atan2_approx(CFixed_23_8 y, CFixed_23_8 x);
#endif // INCLUDED_FIXED