640 lines
14 KiB
Rust
640 lines
14 KiB
Rust
//! Fixed point numbers.
|
|
#![allow(clippy::use_self)]
|
|
#![allow(clippy::cast_lossless)]
|
|
|
|
use std::{fmt::{self, Write}, ops::*};
|
|
|
|
macro_rules! fixed_ref_unop {
|
|
(impl $imp:ident, $method:ident for $t:ty) => {
|
|
impl $imp for &$t
|
|
{
|
|
type Output = <$t as $imp>::Output;
|
|
|
|
#[inline]
|
|
fn $method(self) -> <$t as $imp>::Output {$imp::$method(*self)}
|
|
}
|
|
};
|
|
}
|
|
|
|
macro_rules! fixed_ref_binop {
|
|
(impl $imp:ident, $method:ident for $t:ty, $u:ty) => {
|
|
impl<'a> $imp<$u> for &'a $t
|
|
{
|
|
type Output = <$t as $imp<$u>>::Output;
|
|
|
|
#[inline]
|
|
fn $method(self, o: $u) -> <$t as $imp<$u>>::Output
|
|
{
|
|
$imp::$method(*self, o)
|
|
}
|
|
}
|
|
|
|
impl<'a> $imp<&'a $u> for $t
|
|
{
|
|
type Output = <$t as $imp<$u>>::Output;
|
|
|
|
#[inline]
|
|
fn $method(self, o: &'a $u) -> <$t as $imp<$u>>::Output
|
|
{
|
|
$imp::$method(self, *o)
|
|
}
|
|
}
|
|
|
|
impl<'a, 'b> $imp<&'a $u> for &'b $t
|
|
{
|
|
type Output = <$t as $imp<$u>>::Output;
|
|
|
|
#[inline]
|
|
fn $method(self, o: &'a $u) -> <$t as $imp<$u>>::Output
|
|
{
|
|
$imp::$method(*self, *o)
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
macro_rules! fixed_ref_op_assign {
|
|
(impl $imp:ident, $method:ident for $t:ty, $u:ty) => {
|
|
impl<'a> $imp<&'a $u> for $t
|
|
{
|
|
#[inline]
|
|
fn $method(&mut self, o: &'a $u) {$imp::$method(self, *o);}
|
|
}
|
|
};
|
|
}
|
|
|
|
macro_rules! define_fixed_types {
|
|
(
|
|
$(
|
|
$(#[$outer:meta])*
|
|
struct $t:ident ( $ti:ident, $bytes:expr ) :
|
|
$tu:ident, $tb:ident, $frac_bits:expr; $test:ident
|
|
)*
|
|
) => {$(
|
|
$(#[$outer])*
|
|
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
|
|
#[derive(Copy, Clone, Default, Eq, Ord, PartialEq, PartialOrd)]
|
|
pub struct $t($ti);
|
|
|
|
impl $t
|
|
{
|
|
/// The number of fractional bits in this type.
|
|
#[inline]
|
|
pub const fn frac_bits() -> $tu {$frac_bits}
|
|
|
|
/// The unsigned mask for the fractional bits.
|
|
#[inline]
|
|
pub const fn frac_mask() -> $tu {(1 << $t::frac_bits()) - 1}
|
|
|
|
/// The integer mask for the fractional bits.
|
|
#[inline]
|
|
pub const fn frac_mask_i() -> $ti {(1 << $t::frac_bits()) - 1}
|
|
|
|
/// The representation of `1.0` in this type, unsigned.
|
|
#[inline]
|
|
pub const fn one() -> $tu {1 << $t::frac_bits()}
|
|
|
|
/// The representation of `1.0` in this type, signed.
|
|
#[inline]
|
|
pub const fn one_i() -> $ti {1 << $t::frac_bits()}
|
|
|
|
/// Returns the largest value that can be represented.
|
|
#[inline]
|
|
pub const fn max_value() -> $t {$t($ti::max_value())}
|
|
|
|
/// Returns the smallest value that can be represented.
|
|
#[inline]
|
|
pub const fn min_value() -> $t {$t($ti::min_value())}
|
|
|
|
/// Returns the integer part of a number.
|
|
#[inline]
|
|
pub const fn trunc(self) -> $t {$t(self.0 & !$t::frac_mask_i())}
|
|
|
|
/// Returns the fractional part of a number.
|
|
#[inline]
|
|
pub const fn fract(self) -> $t {$t(self.0 & $t::frac_mask_i())}
|
|
|
|
/// Returns the integer part of a number as an integer.
|
|
#[inline]
|
|
pub const fn integ(self) -> $ti {self.0 >> $t::frac_bits()}
|
|
|
|
/// Returns the number of ones in the bit representation of self.
|
|
#[inline]
|
|
pub const fn count_ones(self) -> u32 {self.0.count_ones()}
|
|
|
|
/// Returns the number of zeros in the bit representation of self.
|
|
#[inline]
|
|
pub const fn count_zeros(self) -> u32 {self.0.count_zeros()}
|
|
|
|
/// Returns the number of leading zeros in the bit representation of
|
|
/// self.
|
|
#[inline]
|
|
pub const fn leading_zeros(self) -> u32 {self.0.leading_zeros()}
|
|
|
|
/// Returns the number of trailing zeros in the bit representation of
|
|
/// self.
|
|
#[inline]
|
|
pub const fn trailing_zeros(self) -> u32 {self.0.trailing_zeros()}
|
|
|
|
/// Rotates all bits left by `n`.
|
|
#[inline]
|
|
pub const fn rotate_left(self, n: u32) -> $t
|
|
{
|
|
$t(self.0.rotate_left(n))
|
|
}
|
|
|
|
/// Rotates all bits right by `n`.
|
|
#[inline]
|
|
pub const fn rotate_right(self, n: u32) -> $t
|
|
{
|
|
$t(self.0.rotate_right(n))
|
|
}
|
|
|
|
/// Reverses the byte order of the bit representation of self.
|
|
#[inline]
|
|
pub const fn swap_bytes(self) -> $t {$t(self.0.swap_bytes())}
|
|
|
|
/// Raises self to the power of `exp`.
|
|
#[inline]
|
|
pub fn pow(self, exp: u32) -> $t {$t(self.0.pow(exp))}
|
|
|
|
/// Returns the absolute value of self.
|
|
#[inline]
|
|
pub fn abs(self) -> $t {$t(self.0.abs())}
|
|
|
|
/// Returns a number representing sign of self.
|
|
#[inline]
|
|
pub fn signum(self) -> $t {$t(self.0.signum() << $t::frac_bits())}
|
|
|
|
/// Returns true if self is positive and false if the number is zero
|
|
/// or negative.
|
|
#[inline]
|
|
pub const fn is_positive(self) -> bool {self.0.is_positive()}
|
|
|
|
/// Returns true if self is negative and false if the number is zero
|
|
/// or positive.
|
|
#[inline]
|
|
pub const fn is_negative(self) -> bool {self.0.is_negative()}
|
|
|
|
/// Return the memory representation of this integer as a byte array
|
|
/// in big-endian (network) byte order.
|
|
#[allow(clippy::missing_const_for_fn)] // HACK: clippy is wrong
|
|
#[inline]
|
|
pub fn to_be_bytes(self) -> [u8; $bytes] {self.0.to_be_bytes()}
|
|
|
|
/// Return the memory representation of this integer as a byte array
|
|
/// in little-endian byte order.
|
|
#[allow(clippy::missing_const_for_fn)] // HACK: clippy is wrong
|
|
#[inline]
|
|
pub fn to_le_bytes(self) -> [u8; $bytes] {self.0.to_le_bytes()}
|
|
|
|
/// Create a value from its representation as a byte array in
|
|
/// big-endian byte order.
|
|
#[allow(clippy::missing_const_for_fn)] // HACK: clippy is wrong
|
|
#[inline]
|
|
pub fn from_be_bytes(b: [u8; $bytes]) -> $t
|
|
{
|
|
$t($ti::from_be_bytes(b))
|
|
}
|
|
|
|
/// Create a value from its representation as a byte array in
|
|
/// little-endian byte order.
|
|
#[allow(clippy::missing_const_for_fn)] // HACK: clippy is wrong
|
|
#[inline]
|
|
pub fn from_le_bytes(b: [u8; $bytes]) -> $t
|
|
{
|
|
$t($ti::from_le_bytes(b))
|
|
}
|
|
|
|
/// Creates a value of this type with the bit pattern `bits`.
|
|
#[inline]
|
|
pub const fn from_bits(bits: $tu) -> $t {$t(bits as $ti)}
|
|
|
|
/// Creates a value of this type with the integral portion `n`.
|
|
#[inline]
|
|
pub const fn from_int(n: $ti) -> $t {$t(n << $t::frac_bits())}
|
|
|
|
/// Creates a value of this type from a fraction.
|
|
#[inline]
|
|
pub const fn from_frac(x: $ti, y: $ti) -> $t
|
|
{
|
|
$t($t::one_i() * x / y + 1)
|
|
}
|
|
|
|
/// Returns the raw bit pattern.
|
|
#[inline]
|
|
pub const fn to_bits(self) -> $tu {self.0 as $tu}
|
|
|
|
/// Sets the raw bit pattern to `bits`.
|
|
#[inline]
|
|
pub fn set_bits(&mut self, bits: $tu) {self.0 = bits as $ti}
|
|
|
|
#[inline]
|
|
const fn mul_i(x: $ti, y: $ti) -> $ti {x * y}
|
|
|
|
#[inline]
|
|
const fn div_i(x: $ti, y: $ti) -> $ti {x / y}
|
|
|
|
#[inline]
|
|
const fn div_k(x: $ti, y: $ti) -> $ti
|
|
{
|
|
(x as $tb * $t::one() as $tb / y as $tb) as $ti
|
|
}
|
|
|
|
#[inline]
|
|
const fn mul_k(x: $ti, y: $ti) -> $ti
|
|
{
|
|
(x as $tb * y as $tb / $t::one() as $tb) as $ti
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod $test {
|
|
use super::$t;
|
|
|
|
#[test]
|
|
fn basic_ops()
|
|
{
|
|
let one = $t::one();
|
|
let two = 2 << $t::frac_bits();
|
|
let twelve = 12 << $t::frac_bits();
|
|
|
|
assert_eq!(($t::from(1) + $t::from(1)).to_bits(), two);
|
|
assert_eq!(($t::from(2) - $t::from(1)).to_bits(), one);
|
|
assert_eq!(($t::from(6) * $t::from(2)).to_bits(), twelve);
|
|
assert_eq!(($t::from(6) * 2) .to_bits(), twelve);
|
|
}
|
|
|
|
#[test]
|
|
fn fractions()
|
|
{
|
|
let three_pt_5 = 3 << $t::frac_bits() | $t::one() / 2;
|
|
let one_pt_2 = 1 << $t::frac_bits() | $t::one() / 5;
|
|
let two_pt_4 = one_pt_2 * 2;
|
|
|
|
assert_eq!(($t::from(7) / $t::from(2)) .to_bits(), three_pt_5);
|
|
assert_eq!(($t::from(7) / 2) .to_bits(), three_pt_5);
|
|
assert_eq!(($t::from_bits(one_pt_2) * 2).to_bits(), two_pt_4);
|
|
}
|
|
|
|
#[test]
|
|
fn printing()
|
|
{
|
|
assert_eq!(format!("{}", $t::from(6)), "6.0");
|
|
assert_eq!(format!("{:2.3}", $t::from(7) / 2), " 3.500");
|
|
}
|
|
}
|
|
|
|
impl From<$ti> for $t
|
|
{
|
|
#[inline]
|
|
fn from(n: $ti) -> $t {$t::from_int(n)}
|
|
}
|
|
|
|
impl Add<$t> for $t
|
|
{
|
|
type Output = $t;
|
|
|
|
#[inline]
|
|
fn add(self, o: $t) -> $t {$t(self.0 + o.0)}
|
|
}
|
|
|
|
fixed_ref_binop! {impl Add, add for $t, $t}
|
|
|
|
impl Sub<$t> for $t
|
|
{
|
|
type Output = $t;
|
|
|
|
#[inline]
|
|
fn sub(self, o: $t) -> $t {$t(self.0 - o.0)}
|
|
}
|
|
|
|
fixed_ref_binop! {impl Sub, sub for $t, $t}
|
|
|
|
impl Mul<$t> for $t
|
|
{
|
|
type Output = $t;
|
|
|
|
#[inline]
|
|
fn mul(self, o: $t) -> $t {$t($t::mul_k(self.0, o.0))}
|
|
}
|
|
|
|
fixed_ref_binop! {impl Mul, mul for $t, $t}
|
|
|
|
impl Mul<$ti> for $t
|
|
{
|
|
type Output = $t;
|
|
|
|
#[inline]
|
|
fn mul(self, o: $ti) -> $t {$t($t::mul_i(self.0, o))}
|
|
}
|
|
|
|
fixed_ref_binop! {impl Mul, mul for $t, $ti}
|
|
|
|
impl Div<$t> for $t
|
|
{
|
|
type Output = $t;
|
|
|
|
#[inline]
|
|
fn div(self, o: $t) -> $t {$t($t::div_k(self.0, o.0))}
|
|
}
|
|
|
|
fixed_ref_binop! {impl Div, div for $t, $t}
|
|
|
|
impl Div<$ti> for $t
|
|
{
|
|
type Output = $t;
|
|
|
|
#[inline]
|
|
fn div(self, o: $ti) -> $t {$t($t::div_i(self.0, o))}
|
|
}
|
|
|
|
fixed_ref_binop! {impl Div, div for $t, $ti}
|
|
|
|
impl BitAnd<$t> for $t
|
|
{
|
|
type Output = $t;
|
|
|
|
#[inline]
|
|
fn bitand(self, o: $t) -> $t {$t(self.0 & o.0)}
|
|
}
|
|
|
|
fixed_ref_binop! {impl BitAnd, bitand for $t, $t}
|
|
|
|
impl BitAnd<$ti> for $t
|
|
{
|
|
type Output = $t;
|
|
|
|
#[inline]
|
|
fn bitand(self, o: $ti) -> $t {$t(self.0 & o)}
|
|
}
|
|
|
|
fixed_ref_binop! {impl BitAnd, bitand for $t, $ti}
|
|
|
|
impl BitOr<$t> for $t
|
|
{
|
|
type Output = $t;
|
|
|
|
#[inline]
|
|
fn bitor(self, o: $t) -> $t {$t(self.0 | o.0)}
|
|
}
|
|
|
|
fixed_ref_binop! {impl BitOr, bitor for $t, $t}
|
|
|
|
impl BitOr<$ti> for $t
|
|
{
|
|
type Output = $t;
|
|
|
|
#[inline]
|
|
fn bitor(self, o: $ti) -> $t {$t(self.0 | o)}
|
|
}
|
|
|
|
fixed_ref_binop! {impl BitOr, bitor for $t, $ti}
|
|
|
|
impl BitXor<$t> for $t
|
|
{
|
|
type Output = $t;
|
|
|
|
#[inline]
|
|
fn bitxor(self, o: $t) -> $t {$t(self.0 ^ o.0)}
|
|
}
|
|
|
|
fixed_ref_binop! {impl BitXor, bitxor for $t, $t}
|
|
|
|
impl BitXor<$ti> for $t
|
|
{
|
|
type Output = $t;
|
|
|
|
#[inline]
|
|
fn bitxor(self, o: $ti) -> $t {$t(self.0 ^ o)}
|
|
}
|
|
|
|
fixed_ref_binop! {impl BitXor, bitxor for $t, $ti}
|
|
|
|
impl Shl<$ti> for $t
|
|
{
|
|
type Output = $t;
|
|
|
|
#[inline]
|
|
fn shl(self, o: $ti) -> $t {$t(self.0 << o)}
|
|
}
|
|
|
|
fixed_ref_binop! {impl Shl, shl for $t, $ti}
|
|
|
|
impl Shr<$ti> for $t
|
|
{
|
|
type Output = $t;
|
|
|
|
#[inline]
|
|
fn shr(self, o: $ti) -> $t {$t(self.0 >> o)}
|
|
}
|
|
|
|
fixed_ref_binop! {impl Shr, shr for $t, $ti}
|
|
|
|
impl AddAssign<$t> for $t
|
|
{
|
|
#[inline]
|
|
fn add_assign(&mut self, o: $t) {self.0 += o.0}
|
|
}
|
|
|
|
fixed_ref_op_assign! {impl AddAssign, add_assign for $t, $t}
|
|
|
|
impl SubAssign<$t> for $t
|
|
{
|
|
#[inline]
|
|
fn sub_assign(&mut self, o: $t) {self.0 -= o.0}
|
|
}
|
|
|
|
fixed_ref_op_assign! {impl SubAssign, sub_assign for $t, $t}
|
|
|
|
impl MulAssign<$t> for $t
|
|
{
|
|
#[inline]
|
|
fn mul_assign(&mut self, o: $t) {self.0 = (*self * o).0}
|
|
}
|
|
|
|
fixed_ref_op_assign! {impl MulAssign, mul_assign for $t, $t}
|
|
|
|
impl MulAssign<$ti> for $t
|
|
{
|
|
#[inline]
|
|
fn mul_assign(&mut self, o: $ti) {self.0 = (*self * o).0}
|
|
}
|
|
|
|
fixed_ref_op_assign! {impl MulAssign, mul_assign for $t, $ti}
|
|
|
|
impl DivAssign<$t> for $t
|
|
{
|
|
#[inline]
|
|
fn div_assign(&mut self, o: $t) {self.0 = (*self / o).0}
|
|
}
|
|
|
|
fixed_ref_op_assign! {impl DivAssign, div_assign for $t, $t}
|
|
|
|
impl DivAssign<$ti> for $t
|
|
{
|
|
#[inline]
|
|
fn div_assign(&mut self, o: $ti) {self.0 = (*self / o).0}
|
|
}
|
|
|
|
fixed_ref_op_assign! {impl DivAssign, div_assign for $t, $ti}
|
|
|
|
impl BitAndAssign<$t> for $t
|
|
{
|
|
#[inline]
|
|
fn bitand_assign(&mut self, o: $t) {self.0 = (*self & o).0}
|
|
}
|
|
|
|
fixed_ref_op_assign! {impl BitAndAssign, bitand_assign for $t, $t}
|
|
|
|
impl BitAndAssign<$ti> for $t
|
|
{
|
|
#[inline]
|
|
fn bitand_assign(&mut self, o: $ti) {self.0 = (*self & o).0}
|
|
}
|
|
|
|
fixed_ref_op_assign! {impl BitAndAssign, bitand_assign for $t, $ti}
|
|
|
|
impl BitOrAssign<$t> for $t
|
|
{
|
|
#[inline]
|
|
fn bitor_assign(&mut self, o: $t) {self.0 = (*self | o).0}
|
|
}
|
|
|
|
fixed_ref_op_assign! {impl BitOrAssign, bitor_assign for $t, $t}
|
|
|
|
impl BitOrAssign<$ti> for $t
|
|
{
|
|
#[inline]
|
|
fn bitor_assign(&mut self, o: $ti) {self.0 = (*self | o).0}
|
|
}
|
|
|
|
fixed_ref_op_assign! {impl BitOrAssign, bitor_assign for $t, $ti}
|
|
|
|
impl BitXorAssign<$t> for $t
|
|
{
|
|
#[inline]
|
|
fn bitxor_assign(&mut self, o: $t) {self.0 = (*self ^ o).0}
|
|
}
|
|
|
|
fixed_ref_op_assign! {impl BitXorAssign, bitxor_assign for $t, $t}
|
|
|
|
impl BitXorAssign<$ti> for $t
|
|
{
|
|
#[inline]
|
|
fn bitxor_assign(&mut self, o: $ti) {self.0 = (*self ^ o).0}
|
|
}
|
|
|
|
fixed_ref_op_assign! {impl BitXorAssign, bitxor_assign for $t, $ti}
|
|
|
|
impl ShlAssign<$ti> for $t
|
|
{
|
|
#[inline]
|
|
fn shl_assign(&mut self, o: $ti) {self.0 = (*self << o).0}
|
|
}
|
|
|
|
fixed_ref_op_assign! {impl ShlAssign, shl_assign for $t, $ti}
|
|
|
|
impl ShrAssign<$ti> for $t
|
|
{
|
|
#[inline]
|
|
fn shr_assign(&mut self, o: $ti) {self.0 = (*self >> o).0}
|
|
}
|
|
|
|
fixed_ref_op_assign! {impl ShrAssign, shr_assign for $t, $ti}
|
|
|
|
impl Neg for $t
|
|
{
|
|
type Output = $t;
|
|
|
|
#[inline]
|
|
fn neg(self) -> $t {$t(-self.0)}
|
|
}
|
|
|
|
fixed_ref_unop! {impl Neg, neg for $t}
|
|
|
|
impl Not for $t
|
|
{
|
|
type Output = $t;
|
|
|
|
#[inline]
|
|
fn not(self) -> $t {$t(!self.0)}
|
|
}
|
|
|
|
fixed_ref_unop! {impl Not, not for $t}
|
|
|
|
impl fmt::Display for $t
|
|
{
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
|
{
|
|
let prec = f.precision().unwrap_or(1);
|
|
let widt = f.width().unwrap_or(0);
|
|
|
|
write!(f, "{:widt$}.", self.0 >> $t::frac_bits(), widt = widt)?;
|
|
|
|
let mut k = self.to_bits();
|
|
|
|
for _ in 0..prec {
|
|
k &= $t::frac_mask();
|
|
k *= 10;
|
|
|
|
let d = k >> $t::frac_bits();
|
|
let d = d % 10;
|
|
|
|
f.write_char(char::from(d as u8 + b'0'))?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for $t
|
|
{
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
|
{
|
|
write!(f,
|
|
concat!(stringify!($t), "::from_bits({})"),
|
|
self.to_bits())
|
|
}
|
|
}
|
|
)*};
|
|
}
|
|
|
|
define_fixed_types! {
|
|
/// A fixed point type representing an angle.
|
|
///
|
|
/// The format of this type is `0.9s`, but because of the implementation,
|
|
/// the real format is `7.9s`.
|
|
struct Angle(i16, 2) : u16, i32, 9; angle_tests
|
|
|
|
/// A fixed point type representing a world unit.
|
|
///
|
|
/// The format of this type is `5.10s`. This has caused eternal suffering.
|
|
struct Unit(i16, 2) : u16, i32, 10; unit_tests
|
|
|
|
/// A generic fixed point type.
|
|
///
|
|
/// The format of this type is `15.16s`.
|
|
struct Fixed(i32, 4) : u32, i64, 16; fixed_tests
|
|
|
|
/// A generic, long fixed point type.
|
|
///
|
|
/// The format of this type is `31.32s`.
|
|
struct FixedLong(i64, 8) : u64, i128, 32; fixed_long_tests
|
|
}
|
|
|
|
impl FixedLong
|
|
{
|
|
/// Creates a value of this type from a `Unit`.
|
|
#[inline]
|
|
pub fn from_unit(n: Unit) -> Self {Self(i64::from(n.to_bits()) << 22)}
|
|
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
#[allow(unused_must_use)]
|
|
fn fixed_overflow() {Fixed::from(i16::max_value() as i32) + Fixed::from(1);}
|
|
|
|
// EOF
|