rewrite fixed point numbers to work better

png-branch
an 2019-03-04 13:45:03 -05:00
parent 403127b7a6
commit 6e4c7512e8
1 changed files with 321 additions and 90 deletions

View File

@ -1,210 +1,441 @@
//! Fixed point numbers.
use std::{fmt::{self, Write},
ops};
use std::{fmt::{self, Write}, ops::*};
macro_rules! define_fixed_type {
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, other: $u) -> <$t as $imp<$u>>::Output
{
$imp::$method(*self, other)
}
}
impl<'a> $imp<&'a $u> for $t
{
type Output = <$t as $imp<$u>>::Output;
#[inline]
fn $method(self, other: &'a $u) -> <$t as $imp<$u>>::Output
{
$imp::$method(self, *other)
}
}
impl<'a, 'b> $imp<&'a $u> for &'b $t
{
type Output = <$t as $imp<$u>>::Output;
#[inline]
fn $method(self, other: &'a $u) -> <$t as $imp<$u>>::Output
{
$imp::$method(*self, *other)
}
}
};
}
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, other: &'a $u) {$imp::$method(self, *other);}
}
};
}
macro_rules! define_fixed_types {
(
$(#[$outer:meta])*
struct $Type:ident ($IT:ident) : $UT:ident, $LT:ident, $FracBits:expr;
) => {
$($(#[$outer:meta])*
struct $t:ident ($ti:ident, $bytes:expr) : $tu:ident, $frac_bits:expr;)*
) => {$(
$(#[$outer])*
#[derive(Copy, Clone, Default, PartialEq, PartialOrd, serde::Serialize)]
pub struct $Type($IT);
pub struct $t($ti);
impl $Type
impl $t
{
/// The number of fractional bits in this type.
pub const FRACBITS: $UT = $FracBits;
#[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.
pub const FRACMASK: $UT = (1 << Self::FRACBITS) - 1;
#[inline]
pub const fn frac_mask_i() -> $ti {(1 << $t::frac_bits()) - 1}
/// The representation of `1.0` in this type.
pub const ONE: $IT = 1 << Self::FRACBITS;
#[inline]
pub const fn one() -> $tu {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 number of ones in the bit representation of self.
#[inline]
pub fn count_ones(self) -> u32 {self.0.count_ones()}
/// Returns the number of zeros in the bit representation of self.
#[inline]
pub fn count_zeros(self) -> u32 {self.0.count_zeros()}
/// Returns the number of leading zeros in the bit representation of
/// self.
#[inline]
pub fn leading_zeros(self) -> u32 {self.0.leading_zeros()}
/// Returns the number of trailing zeros in the bit representation of
/// self.
#[inline]
pub fn trailing_zeros(self) -> u32 {self.0.trailing_zeros()}
/// Rotates all bits left by `n`.
#[inline]
pub fn rotate_left(self, n: u32) -> $t {$t(self.0.rotate_left(n))}
/// Rotates all bits right by `n`.
#[inline]
pub 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 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 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 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.
#[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.
#[inline]
pub fn to_le_bytes(self) -> [u8; $bytes] {self.0.to_le_bytes()}
/// Return the memory representation of this integer as a byte array
/// in native byte order.
#[inline]
pub fn to_ne_bytes(self) -> [u8; $bytes] {self.0.to_ne_bytes()}
/// Create a value from its representation as a byte array in
/// big-endian byte order.
#[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.
#[inline]
pub fn from_le_bytes(b: [u8; $bytes]) -> $t
{
$t($ti::from_le_bytes(b))
}
/// Create a value from its representation as a byte array in
/// native byte order.
#[inline]
pub fn from_ne_bytes(b: [u8; $bytes]) -> $t
{
$t($ti::from_ne_bytes(b))
}
/// Creates a value of this type with the bit pattern `bits`.
#[inline]
pub const fn from_bits(bits: $UT) -> Self {$Type(bits as $IT)}
pub const fn from_bits(bits: $tu) -> $t {$t(bits as $ti)}
/// Creates a value of this type with the integral portion `n`.
#[inline]
#[allow(clippy::cast_lossless)]
pub const fn from_int(n: $LT) -> Self
{
$Type(n as ($IT) << Self::FRACBITS)
}
pub const fn from_int(n: $ti) -> $t {$t(n << $t::frac_bits())}
/// Returns the raw bit pattern.
#[inline]
pub fn to_bits(&self) -> $UT {self.0 as $UT}
pub fn to_bits(self) -> $tu {self.0 as $tu}
/// Sets the raw bit pattern to `bits`.
#[inline]
pub fn set_bits(&mut self, bits: $UT) {self.0 = bits as $IT}
/// Returns the integral portion.
#[inline]
pub fn integ(&self) -> $LT {(self.0 >> Self::FRACBITS) as $LT}
/// Returns the fractional portion.
///
/// This is a value of `1` to `FRACMASK`.
#[allow(trivial_numeric_casts)]
pub fn fract(&self) -> u16 {(self.0 as $UT & Self::FRACMASK) as u16}
/// Returns a multiplication by integer.
///
/// Panics if the result overflows.
#[inline]
pub fn mul_i(&self, n: $LT) -> Self {$Type(self.0 * $IT::from(n))}
/// Returns a division by integer.
///
/// Panics if `n` is `0`.
#[inline]
pub fn div_i(&self, n: $LT) -> Self {$Type(self.0 / $IT::from(n))}
pub fn set_bits(&mut self, bits: $tu) {self.0 = bits as $ti}
#[inline]
fn fx_div(x: $IT, y: $IT) -> $IT
const fn mul_i(x: $ti, y: $ti) -> $ti {x * y}
#[inline]
const fn div_i(x: $ti, y: $ti) -> $ti {x / y}
#[inline]
fn div_k(x: $ti, y: $ti) -> $ti
{
(i64::from(x) * i64::from(Self::ONE) / i64::from(y)) as $IT
(i64::from(x) * i64::from($t::one()) / i64::from(y)) as $ti
}
#[inline]
fn fx_mul(x: $IT, y: $IT) -> $IT
fn mul_k(x: $ti, y: $ti) -> $ti
{
(i64::from(x) * i64::from(y) / i64::from(Self::ONE)) as $IT
(i64::from(x) * i64::from(y) / i64::from($t::one())) as $ti
}
}
impl From<$LT> for $Type
impl From<$ti> for $t
{
#[inline]
fn from(n: $LT) -> Self {$Type::from_int(n)}
fn from(n: $ti) -> $t {$t::from_int(n)}
}
impl ops::Add for $Type
impl Add<$t> for $t
{
type Output = Self;
type Output = $t;
#[inline]
fn add(self, o: Self) -> Self {$Type(self.0 + o.0)}
fn add(self, o: $t) -> $t {$t(self.0 + o.0)}
}
impl ops::Sub for $Type
fixed_ref_binop! {impl Add, add for $t, $t}
impl Sub<$t> for $t
{
type Output = Self;
type Output = $t;
#[inline]
fn sub(self, o: Self) -> Self {$Type(self.0 - o.0)}
fn sub(self, o: $t) -> $t {$t(self.0 - o.0)}
}
impl ops::Mul for $Type
fixed_ref_binop! {impl Sub, sub for $t, $t}
impl Mul<$t> for $t
{
type Output = Self;
type Output = $t;
#[inline]
fn mul(self, o: Self) -> Self {$Type(Self::fx_mul(self.0, o.0))}
fn mul(self, o: $t) -> $t {$t($t::mul_k(self.0, o.0))}
}
impl ops::Div for $Type
fixed_ref_binop! {impl Mul, mul for $t, $t}
impl Mul<$ti> for $t
{
type Output = Self;
type Output = $t;
#[inline]
fn div(self, o: Self) -> Self {$Type(Self::fx_div(self.0, o.0))}
fn mul(self, o: $ti) -> $t {$t($t::mul_i(self.0, o))}
}
impl ops::Neg for $Type
fixed_ref_binop! {impl Mul, mul for $t, $ti}
impl Div<$t> for $t
{
type Output = Self;
type Output = $t;
#[inline]
fn neg(self) -> Self {$Type(-self.0)}
fn div(self, o: $t) -> $t {$t($t::div_k(self.0, o.0))}
}
impl ops::Not for $Type
fixed_ref_binop! {impl Div, div for $t, $t}
impl Div<$ti> for $t
{
type Output = Self;
type Output = $t;
#[inline]
fn not(self) -> Self {$Type(!self.0)}
fn div(self, o: $ti) -> $t {$t($t::div_i(self.0, o))}
}
impl fmt::Display for $Type
fixed_ref_binop! {impl Div, div 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 AddAssign for $t
{
#[inline]
fn add_assign(&mut self, other: $t) {self.0 += other.0}
}
fixed_ref_op_assign! {impl AddAssign, add_assign for $t, $t}
impl SubAssign for $t
{
#[inline]
fn sub_assign(&mut self, other: $t) {self.0 -= other.0}
}
fixed_ref_op_assign! {impl SubAssign, sub_assign for $t, $t}
impl MulAssign for $t
{
#[inline]
fn mul_assign(&mut self, other: $t) {self.0 = (*self * other).0}
}
fixed_ref_op_assign! {impl MulAssign, mul_assign for $t, $t}
impl DivAssign for $t
{
#[inline]
fn div_assign(&mut self, other: $t) {self.0 = (*self / other).0}
}
fixed_ref_op_assign! {impl DivAssign, div_assign for $t, $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.integ(), widt = widt)?;
write!(f, "{:widt$}.", self.0 >> $t::frac_bits(), widt = widt)?;
let mut k = self.to_bits();
for _ in 0..prec {
k &= Self::FRACMASK;
k &= $t::frac_mask();
k *= 10;
let d = k >> Self::FRACBITS;
let d = k >> $t::frac_bits();
let d = d % 10;
f.write_char((d as u8 + b'0') as char)?;
f.write_char(char::from(d as u8 + b'0'))?;
}
Ok(())
}
}
impl fmt::Debug for $Type
impl fmt::Debug for $t
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
fmt::Display::fmt(self, f)
}
}
};
)*};
}
define_fixed_type! {
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) : u16, i8, 9;
}
struct Angle(i16, 2) : u16, 9;
define_fixed_type! {
/// A fixed point type representing a world unit.
///
/// The format of this type is `5.10s`. This has caused eternal suffering.
struct Unit(i16) : u16, i8, 10;
}
struct Unit(i16, 2) : u16, 10;
define_fixed_type! {
/// A generic fixed point type.
///
/// The format of this type is `15.16s`.
struct Fixed(i32) : u32, i16, 16;
struct Fixed(i32, 4) : u32, 16;
}
#[test]
fn fixed_basic_ops()
{
let seven_div_2 = 3 << Fixed::FRACBITS | Fixed::FRACMASK / 2 + 1;
assert_eq!((Fixed::from(1) + 1.into()).to_bits(), 2 << Fixed::FRACBITS);
assert_eq!((Fixed::from(2) - 1.into()).to_bits(), 1 << Fixed::FRACBITS);
assert_eq!((Fixed::from(6) * 2.into()).to_bits(), 12 << Fixed::FRACBITS);
assert_eq!((Fixed::from(6).mul_i(2)) .to_bits(), 12 << Fixed::FRACBITS);
assert_eq!((Fixed::from(7) / 2.into()).to_bits(), seven_div_2);
assert_eq!((Fixed::from(7).div_i(2)) .to_bits(), seven_div_2);
let one = Fixed::one();
let two = 2 << Fixed::frac_bits();
let twelve = 12 << Fixed::frac_bits();
assert_eq!((Fixed::from(1) + Fixed::from(1)).to_bits(), two);
assert_eq!((Fixed::from(2) - Fixed::from(1)).to_bits(), one);
assert_eq!((Fixed::from(6) * Fixed::from(2)).to_bits(), twelve);
assert_eq!((Fixed::from(6) * 2) .to_bits(), twelve);
}
#[test]
fn fixed_fractions()
{
let three_pt_5 = 3 << Fixed::frac_bits() | Fixed::one() / 2;
let one_pt_2 = 1 << Fixed::frac_bits() | Fixed::one() / 5;
let two_pt_4 = 157_286;
assert_eq!((Fixed::from(7) / Fixed::from(2)).to_bits(), three_pt_5);
assert_eq!((Fixed::from(7) / 2) .to_bits(), three_pt_5);
assert_eq!((Fixed::from_bits(one_pt_2) * 2) .to_bits(), two_pt_4);
}
#[test]
#[should_panic]
#[allow(unused_must_use)]
fn fixed_overflow() {Fixed::from(i16::max_value()) + 1.into();}
fn fixed_overflow() {Fixed::from(i16::max_value() as i32) + Fixed::from(1);}
#[test]
fn fixed_printing()
{
assert_eq!(format!("{}", Fixed::from(6)), "6.0");
assert_eq!(format!("{}", Fixed::from(7).div_i(2)), "3.5");
assert_eq!(format!("{}", Fixed::from(6)), "6.0");
assert_eq!(format!("{}", Fixed::from(7) / 2), "3.5");
assert_eq!(format!("{:7.7}", Fixed::from_bits(0xDEAD_BEEF)),
" -8531.7458343");
}