working fixed point ???

This commit is contained in:
2024-10-29 14:31:51 -04:00
parent 74bde02cfa
commit 5513ddf25b
6 changed files with 534 additions and 354 deletions

View File

@@ -1,25 +1,13 @@
#![feature(bigint_helper_methods)]
use client::ClientApp;
use util::FixedDec;
mod client;
mod util;
fn main() {
let a = FixedDec::from(0.75);
println!("a = {}", a);
let b = FixedDec::from(1.75);
println!("b = {}", b);
println!("a + b = {}", &a + &b);
let c = FixedDec::from(1.0 / 16.0);
println!("c = {}", c);
println!("a + c = {}", &a + &c);
println!("-a = {}", -&a);
println!("b - a = {}", &b - &a);
println!("-c = {}", -&c);
// let event_loop = winit::event_loop::EventLoop::new().expect("Failed to create event loop");
// event_loop
// .run_app(&mut ClientApp::new())
// .expect("Failed to run event loop");
let event_loop = winit::event_loop::EventLoop::new().expect("Failed to create event loop");
event_loop
.run_app(&mut ClientApp::new())
.expect("Failed to run event loop");
}

View File

@@ -1,338 +0,0 @@
use num_traits::Zero;
use std::{
fmt::{Binary, Debug, Display},
ops::{Add, AddAssign, Mul, Neg, Shr, Sub, SubAssign},
};
const POS: bool = false;
const NEG: bool = true;
#[derive(Debug, Clone, PartialEq)]
pub struct FixedDec {
sign: bool,
dec: i32,
parts: Vec<u32>,
}
impl FixedDec {
pub fn zeros() -> Self {
Self::zero()
}
pub fn dec_len(&self) -> i32 {
self.parts.len() as i32 - self.dec
}
pub fn part(&self, i: i32) -> u32 {
let Ok(i): Result<usize, _> = i.try_into() else {
return self.pre_padding();
};
self.parts.get(i).cloned().unwrap_or(0)
}
pub fn is_pos(&self) -> bool {
!self.sign
}
pub fn is_neg(&self) -> bool {
self.sign
}
fn pre_padding(&self) -> u32 {
match self.sign {
POS => 0,
NEG => !0,
}
}
}
impl Zero for FixedDec {
fn zero() -> Self {
Self {
sign: POS,
dec: 0,
parts: Vec::new(),
}
}
fn is_zero(&self) -> bool {
self.parts.iter().all(|&b| b == 0)
}
}
impl Shr<u32> for FixedDec {
type Output = Self;
fn shr(self, rhs: u32) -> Self::Output {
let mut parts = Vec::new();
let sr = rhs % 32;
let sl = 32 - sr;
let mask = (1 << sr) - 1;
let dec = self.dec - (rhs / 32) as i32;
let mut rem = 0;
for part in self.parts {
parts.push((part >> sr) ^ rem);
rem = (part & mask) << sl;
}
if rem != 0 {
parts.push(rem);
}
Self {
dec,
parts,
sign: self.sign,
}
}
}
impl Add for FixedDec {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
&self + &rhs
}
}
impl AddAssign<&FixedDec> for FixedDec {
fn add_assign(&mut self, rhs: &FixedDec) {
let dec = self.dec.max(rhs.dec);
let left_i = -dec;
let right_i = self.dec_len().max(rhs.dec_len());
let len = (right_i - left_i) as usize;
if dec != self.dec {
let fill = self.pre_padding();
let fill_len = rhs.dec - self.dec;
self.parts.splice(0..0, (0..fill_len).map(|_| fill));
self.dec += fill_len;
}
if self.parts.len() != len {
self.parts.resize(len, 0);
}
let mut carry = false;
let rhs_offset = rhs.dec - self.dec;
for i in (0..self.parts.len()).rev() {
let a = self.parts[i];
let b = rhs.part(i as i32 + rhs_offset);
let (res, c) = a.carrying_add(b, carry);
self.parts[i] = res;
carry = c;
}
let sign = if self.sign == rhs.sign {
if self.sign == POS && carry {
self.parts.insert(0, 1);
self.dec += 1;
} else if self.sign == NEG && !carry {
self.parts.insert(0, !1);
self.dec += 1;
}
self.sign
} else if carry {
POS
} else {
NEG
};
self.sign = sign;
}
}
impl SubAssign<&FixedDec> for FixedDec {
fn sub_assign(&mut self, rhs: &FixedDec) {
*self += &-rhs;
}
}
impl Add for &FixedDec {
type Output = FixedDec;
fn add(self, rhs: Self) -> Self::Output {
let mut dec = self.dec.max(rhs.dec);
let left_i = -dec;
let right_i = self.dec_len().max(rhs.dec_len());
let mut parts = Vec::with_capacity((right_i - left_i) as usize);
let mut carry = false;
for i in (left_i..right_i).rev() {
let a = self.part(i + self.dec);
let b = rhs.part(i + rhs.dec);
let (res, c) = a.carrying_add(b, carry);
parts.push(res);
carry = c;
}
let sign = if self.sign == rhs.sign {
if self.is_pos() && carry {
parts.push(1);
dec += 1;
} else if self.is_neg() && !carry {
parts.push(!1);
dec += 1;
}
self.sign
} else if carry {
POS
} else {
NEG
};
parts.reverse();
FixedDec { parts, dec, sign }
}
}
impl Sub for &FixedDec {
type Output = FixedDec;
fn sub(self, rhs: Self) -> Self::Output {
self + &(-rhs)
}
}
impl Neg for &FixedDec {
type Output = FixedDec;
fn neg(self) -> Self::Output {
let parts = self.parts.iter().map(|p| !p).collect();
let mut res = FixedDec {
parts,
sign: !self.sign,
dec: self.dec,
};
res += &Self::Output {
parts: vec![1],
dec: self.dec - (self.parts.len() as i32 - 1),
sign: POS,
};
res
}
}
impl Mul for &FixedDec {
type Output = FixedDec;
fn mul(self, rhs: Self) -> Self::Output {
let mut parts: Vec<u32> = vec![0; self.parts.len() + rhs.parts.len()];
for (i, &x) in self.parts.iter().enumerate().rev() {
let mut carry: u32 = 0;
for (j, &y) in rhs.parts.iter().enumerate().rev() {
let (lsb, msb) = mul_lmsb(x, y);
let k = i + j + 1;
let (res, carry1) = parts[k].overflowing_add(lsb);
parts[k] = res;
let (res, carry2) = parts[k].overflowing_add(carry);
parts[k] = res;
// dude I have no clue if this can overflow; I know msb can take 1 without
// overflowing, but I'm not sure if 2 can get here when it's max
carry = (carry1 as u32) + (carry2 as u32) + msb;
}
if carry > 0 {
parts[i] = carry;
}
}
Self::Output {
dec: self.dec + rhs.dec,
parts,
sign: self.sign == rhs.sign,
}
}
}
fn mul_lmsb(x: u32, y: u32) -> (u32, u32) {
let lsb = x.wrapping_mul(y);
let a = x & 0xffff;
let b = x >> 16;
let c = y & 0xffff;
let d = y >> 16;
let ad = a * d + ((a * c) >> 16);
let bc = b * c;
let car = ad > (0xffffffff - bc);
let msb = ((ad + bc) >> 16) + ((car as u32) << 16) + b * d;
(lsb, msb)
}
const INV_SIGN_MASK: u32 = (1 << 31) - 1;
const FRAC_BIT: u32 = 1 << 23;
const FRAC_MASK: u32 = FRAC_BIT - 1;
impl From<f32> for FixedDec {
fn from(value: f32) -> Self {
let raw = value.to_bits() & INV_SIGN_MASK;
let exp = (raw >> 23) as i32 - 127;
let frac = (raw & FRAC_MASK) + FRAC_BIT;
let start = -exp - 1;
let end = -exp + 23;
let start_i = start.div_euclid(32);
let end_i = (end - 1).div_euclid(32);
let parts = if start_i == end_i {
vec![frac << (8 - start.rem_euclid(32))]
} else {
let s = end.rem_euclid(32);
vec![frac >> s, frac << (32 - s)]
};
Self {
sign: POS,
dec: -start_i,
parts,
}
}
}
impl From<&FixedDec> for f32 {
fn from(value: &FixedDec) -> Self {
let mut sign = 0;
let value = if value.is_neg() {
sign = 1 << 31;
&-value
} else {
value
};
let mut skip_count = 0;
let mut iter = value
.parts
.iter()
.inspect(|_| skip_count += 1)
.skip_while(|&&x| x == 0);
let Some(v) = iter.next() else {
return 0.0;
};
let start = v.leading_zeros();
let frac = if start > 9 {
let sh = start - 9;
(v << sh) + iter.next().copied().map(|v| v >> (32 - sh)).unwrap_or(0)
} else {
v >> (9 - start)
};
let exp = (127 - (skip_count * 32 + start)) << 23;
let res = frac + exp + sign;
println!();
println!("res: {:032b}", res);
println!("ans: {:032b}", 0.75f32.to_bits());
f32::from_bits(res)
}
}
impl Display for FixedDec {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", f32::from(self))
}
}
impl Binary for FixedDec {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.sign == NEG {
write!(f, "-")?;
}
if self.dec < 0 {
write!(f, ".")?;
for _ in 0..(-self.dec) {
write!(f, "00000000000000000000000000000000")?;
}
}
for (i, part) in self.parts.iter().enumerate() {
if i as i32 == self.dec {
write!(f, ".")?;
} else if i != 0 {
write!(f, "_")?;
}
write!(f, "{:032b}", part)?;
}
Ok(())
}
}

View File

@@ -0,0 +1,117 @@
use num_traits::Zero;
use super::{FixedDec, POS};
const INV_SIGN_MASK: u32 = (1 << 31) - 1;
const FRAC_BIT: u32 = 1 << 23;
const FRAC_MASK: u32 = FRAC_BIT - 1;
impl From<f32> for FixedDec {
fn from(value: f32) -> Self {
let raw = value.to_bits() & INV_SIGN_MASK;
let mut exp = (raw >> 23) as i32 - 127;
let mut frac = raw & FRAC_MASK;
let mut start = -exp;
if exp == -127 {
exp = -126;
start = -exp;
} else {
frac += FRAC_BIT;
start -= 1;
}
let end = -exp + 23;
let start_i = start.div_euclid(32);
let end_i = (end - 1).div_euclid(32);
let mut parts = Vec::new();
let mut dec = -start_i;
if start_i == end_i {
let val = frac << (8 - start.rem_euclid(32));
if val != 0 {
parts.push(val);
}
} else {
let s = end.rem_euclid(32);
let val_high = frac >> s;
let val_low = frac << (32 - s);
if val_high != 0 {
parts.push(val_high);
} else {
dec -= 1;
}
if val_low != 0 {
parts.push(val_low);
}
}
if parts.is_empty() {
dec = 0;
}
let s = Self {
sign: POS,
dec,
parts,
};
if value.is_sign_negative() {
-&s
} else {
s
}
}
}
impl From<FixedDec> for f32 {
fn from(value: FixedDec) -> Self {
Self::from(&value)
}
}
impl From<&FixedDec> for f32 {
fn from(value: &FixedDec) -> Self {
if value.is_zero() {
return if value.sign == POS { 0.0 } else { -0.0 };
}
let mut sign = 0;
let value = if value.is_neg() {
sign = 1 << 31;
&-value
} else {
value
};
let mut skip_count = 0;
let mut iter = value.parts.iter().peekable();
while let Some(0) = iter.peek() {
skip_count += 1;
iter.next();
}
let Some(v) = iter.next() else {
return 0.0;
};
let mut start = v.leading_zeros() + 1;
let exp_i = (value.dec - skip_count) * 32 - start as i32;
let mut frac_sh = 0;
let exp = if exp_i >= -127 {
if exp_i == -127 {
start -= 1;
}
(exp_i + 127) as u32
} else {
let sh = -(exp_i + 32 * 4 - 1);
if sh < 23 {
start -= 1;
frac_sh = sh;
0
} else {
return 0.0;
}
};
let frac = if start > 9 {
let sh = start - 9;
(v << sh) + iter.next().copied().map(|v| v >> (32 - sh)).unwrap_or(0)
} else {
v >> (9 - start)
} & !(1 << 23);
let res = (frac >> frac_sh) + (exp << 23) + sign;
f32::from_bits(res)
}
}

87
src/util/fixed/mod.rs Normal file
View File

@@ -0,0 +1,87 @@
mod conversion;
mod op;
#[cfg(test)]
mod test;
use num_traits::Zero;
use std::fmt::{Binary, Display};
const POS: bool = false;
const NEG: bool = true;
#[derive(Debug, Clone, PartialEq)]
pub struct FixedDec {
sign: bool,
dec: i32,
parts: Vec<u32>,
}
impl FixedDec {
pub fn zeros() -> Self {
Self::zero()
}
pub fn dec_len(&self) -> i32 {
self.parts.len() as i32 - self.dec
}
pub fn part(&self, i: i32) -> u32 {
let Ok(i): Result<usize, _> = i.try_into() else {
return 0;
};
self.parts.get(i).cloned().unwrap_or(0)
}
pub fn is_pos(&self) -> bool {
!self.sign
}
pub fn is_neg(&self) -> bool {
self.sign
}
pub fn trim(&mut self) {
let rem_beg = self
.parts
.iter()
.take_while(|&&x| x == 0)
.count();
self.parts.drain(0..rem_beg);
let rem_end = self.parts.iter().rev().take_while(|&&x| x == 0).count();
self.parts.truncate(self.parts.len() - rem_end);
if self.parts.is_empty() {
self.dec = 0;
} else {
self.dec -= rem_beg as i32;
}
}
}
impl Display for FixedDec {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", f32::from(self))
}
}
impl Binary for FixedDec {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.sign == NEG {
write!(f, "-")?;
}
if self.dec < 0 {
write!(f, ".")?;
for _ in 0..(-self.dec) {
write!(f, "00000000000000000000000000000000")?;
}
}
for (i, part) in self.parts.iter().enumerate() {
if i as i32 == self.dec {
write!(f, ".")?;
} else if i != 0 {
write!(f, "_")?;
}
write!(f, "{:032b}", part)?;
}
Ok(())
}
}

230
src/util/fixed/op.rs Normal file
View File

@@ -0,0 +1,230 @@
use super::*;
use std::ops::{Add, AddAssign, Mul, Neg, Shr, Sub, SubAssign};
impl Zero for FixedDec {
fn zero() -> Self {
Self {
sign: POS,
dec: 0,
parts: Vec::new(),
}
}
fn is_zero(&self) -> bool {
self.parts.iter().all(|&b| b == 0)
}
}
impl Shr<u32> for FixedDec {
type Output = Self;
fn shr(self, rhs: u32) -> Self::Output {
let mut parts = Vec::new();
let sr = rhs % 32;
let sl = 32 - sr;
let mask = (1 << sr) - 1;
let dec = self.dec - (rhs / 32) as i32;
let mut rem = 0;
for part in self.parts {
parts.push((part >> sr) ^ rem);
rem = (part & mask) << sl;
}
if rem != 0 {
parts.push(rem);
}
Self {
dec,
parts,
sign: self.sign,
}
}
}
impl Add for FixedDec {
type Output = Self;
fn add(mut self, rhs: Self) -> Self::Output {
self += &rhs;
self
}
}
impl AddAssign<FixedDec> for FixedDec {
fn add_assign(&mut self, rhs: FixedDec) {
*self += &rhs
}
}
impl AddAssign<&FixedDec> for FixedDec {
fn add_assign(&mut self, rhs: &FixedDec) {
let (dec, len) = new_dec(self, rhs);
if dec != self.dec {
let fill_len = rhs.dec - self.dec;
self.parts.splice(0..0, (0..fill_len).map(|_| 0));
self.dec += fill_len;
}
if self.parts.len() != len {
self.parts.resize(len, 0);
}
let src = trust(&self.parts);
add(self, &|i| src[i], rhs);
}
}
pub fn trust<'b, T>(x: &T) -> &'b T {
unsafe { std::mem::transmute(x) }
}
impl Add for &FixedDec {
type Output = FixedDec;
fn add(self, rhs: Self) -> Self::Output {
let (dec, len) = new_dec(self, rhs);
let mut parts = Vec::with_capacity(len);
#[allow(clippy::uninit_vec)]
unsafe {
parts.set_len(len);
}
let mut res = FixedDec {
sign: self.sign,
dec,
parts,
};
#[allow(clippy::suspicious_arithmetic_impl)]
let offset = self.dec - dec;
add(&mut res, &|i| self.part(i as i32 + offset), rhs);
res
}
}
fn add(dest: &mut FixedDec, at: &impl Fn(usize) -> u32, rhs: &FixedDec) {
let mut carry = false;
let same_sign = dest.sign == rhs.sign;
let rhs_offset = rhs.dec - dest.dec;
for i in (0..dest.parts.len()).rev() {
let a = at(i);
let b = rhs.part(i as i32 + rhs_offset);
(dest.parts[i], carry) = carry_add(a, b, same_sign, carry);
}
if same_sign {
if carry {
dest.parts.insert(0, 1);
dest.dec += 1;
}
} else if carry {
dest.sign = !dest.sign
}
}
fn new_dec(x: &FixedDec, y: &FixedDec) -> (i32, usize) {
let dec = x.dec.max(y.dec);
let left_i = -dec;
let right_i = x.dec_len().max(y.dec_len());
let len = (right_i - left_i) as usize;
(dec, len)
}
fn carry_add(a: u32, b: u32, same_sign: bool, carry: bool) -> (u32, bool) {
if same_sign {
a.carrying_add(b, carry)
} else {
let (res, c) = a.overflowing_sub(b);
let (res, c2) = res.overflowing_sub(carry as u32);
(res, c || c2)
}
}
impl Sub for &FixedDec {
type Output = FixedDec;
fn sub(self, rhs: Self) -> Self::Output {
self + &(-rhs)
}
}
impl Sub for FixedDec {
type Output = Self;
fn sub(mut self, rhs: Self) -> Self::Output {
self += &(-rhs);
self
}
}
impl SubAssign<&FixedDec> for FixedDec {
fn sub_assign(&mut self, rhs: &FixedDec) {
*self += &-rhs;
}
}
impl Neg for &FixedDec {
type Output = FixedDec;
fn neg(self) -> Self::Output {
let mut res = self.clone();
res.sign = !res.sign;
res
}
}
impl Neg for FixedDec {
type Output = Self;
fn neg(mut self) -> Self::Output {
self.sign = !self.sign;
self
}
}
impl Mul for &FixedDec {
type Output = FixedDec;
fn mul(self, rhs: Self) -> Self::Output {
let sign = self.sign != rhs.sign;
let mut parts: Vec<u32> = vec![0; self.parts.len() + rhs.parts.len()];
let dec = self.dec + rhs.dec;
for (i, &x) in self.parts.iter().enumerate().rev() {
let mut carry: u32 = 0;
for (j, &y) in rhs.parts.iter().enumerate().rev() {
let (lsb, msb) = x.widening_mul(y);
// let (lsb, msb) = mul_lmsb(x, y);
let k = i + j + 1;
let (res, carry1) = parts[k].overflowing_add(lsb);
parts[k] = res;
let (res, carry2) = parts[k].overflowing_add(carry);
parts[k] = res;
// dude I have no clue if this can overflow; I know msb can take 1 without
// overflowing, but I'm not sure if 2 can get here when it's max
carry = (carry1 as u32) + (carry2 as u32) + msb;
}
parts[i] = carry
}
let mut res = Self::Output { dec, parts, sign };
res.trim();
res
}
}
impl Mul for FixedDec {
type Output = FixedDec;
fn mul(self, rhs: Self) -> Self::Output {
&self * &rhs
}
}
fn mul_lmsb(x: u32, y: u32) -> (u32, u32) {
let lsb = x.wrapping_mul(y);
let a = x & 0xffff;
let b = x >> 16;
let c = y & 0xffff;
let d = y >> 16;
let ad = a * d + ((a * c) >> 16);
let bc = b * c;
let carry = ad > (0xffffffff - bc);
let msb = ((ad + bc) >> 16) + ((carry as u32) << 16) + b * d;
(lsb, msb)
}

96
src/util/fixed/test.rs Normal file
View File

@@ -0,0 +1,96 @@
use super::*;
macro_rules! assert_bits_eq {
($left:expr, $right:expr, $dec:expr $(,)?) => {
assert!(
$left == $right,
"\n left: {:032b} = {:?}\n right: {:032b} = {:?}\n from: {:?}",
$left.to_bits(),
$left,
$right.to_bits(),
$right,
$dec,
)
};
($left:expr, $right:expr, $dec:expr, $arg:tt, $($args:tt)+) => {
assert!(
$left == $right,
concat!("\n expr: ", $arg, "\n left: {:032b} = {:?}\n right: {:032b} = {:?}\n from: {:?}"),
$($args)+,
$left.to_bits(),
$left,
$right.to_bits(),
$right,
$dec,
)
}
}
#[test]
fn conversion() {
fn test(x: f32) {
let dec = FixedDec::from(x);
assert_bits_eq!(x, f32::from(&dec), dec)
}
test(f32::from_bits(0b00000000_00000000_00000000_00000001));
test(f32::from_bits(0b00000000_01000000_00000000_00000001));
test(f32::from_bits(0b00010000_01000000_00000000_00000001));
test(f32::from_bits(0b10000000_00000000_00000000_00000001));
test(f32::from_bits(0b10000100_00010000_00010000_00100001));
test(f32::from_bits(0b10000100_00000000_00000000_00000000));
test(f32::from_bits(0b00111111_11111111_11111111_11111111));
test(f32::from_bits(0b10111111_11111111_11111111_11111111));
test(0.75 + 0.125);
test(1.75);
test(1.0 / 16.0);
test(3.75);
test(-3.75);
test(1000000000.75);
test(-1000000000.75);
assert!(FixedDec::from(0.0).is_zero());
test(-1.0);
}
#[test]
fn add_sub() {
fn test_add(x: f32, y: f32) {
let a = x + y;
let dec = FixedDec::from(x) + FixedDec::from(y);
assert_bits_eq!(a, f32::from(&dec), dec, "{} + {}", x, y);
}
test_add(0.25, 0.75);
test_add(1.25, 0.125);
test_add(1.25, -0.125);
test_add(100.25, -0.125);
test_add(-1.25, 0.125);
test_add(-100.25, -0.125);
test_add(100.25, -0.125);
// test_add(0.25, -0.00000000125);
test_add(0.25, -0.0000125);
test_add(100000000000000.0, -100000000000.0);
}
#[test]
fn mul() {
fn test_mul(x: f32, y: f32) {
let a = x * y;
let dec = FixedDec::from(x) * FixedDec::from(y);
assert_bits_eq!(a, f32::from(&dec), dec, "{:?} * {:?}", x, y);
}
test_mul(0.0, 0.0);
test_mul(1.0, 1.0);
test_mul(1.0, 0.0);
test_mul(2.0, 1.0);
test_mul(2.0, 0.5);
test_mul(20.0, 0.245);
test_mul(0.03819, 0.0183488);
test_mul(30492.39, 9130.391);
test_mul(2.0, -1.0);
test_mul(0.0, -1.0);
test_mul(-1.0, 0.0);
test_mul(1.0, -1.0);
test_mul(2.0, -1.20904);
test_mul(-249.0, -1.20904);
test_mul(-30492.39, 9130.391);
}