From 5513ddf25b5aef5ae62474b1b6a53efea641e083 Mon Sep 17 00:00:00 2001 From: Shadow Cat Date: Tue, 29 Oct 2024 14:31:51 -0400 Subject: [PATCH] working fixed point ??? --- src/main.rs | 20 +-- src/util/fixed.rs | 338 ----------------------------------- src/util/fixed/conversion.rs | 117 ++++++++++++ src/util/fixed/mod.rs | 87 +++++++++ src/util/fixed/op.rs | 230 ++++++++++++++++++++++++ src/util/fixed/test.rs | 96 ++++++++++ 6 files changed, 534 insertions(+), 354 deletions(-) delete mode 100644 src/util/fixed.rs create mode 100644 src/util/fixed/conversion.rs create mode 100644 src/util/fixed/mod.rs create mode 100644 src/util/fixed/op.rs create mode 100644 src/util/fixed/test.rs diff --git a/src/main.rs b/src/main.rs index b8ef9e2..06ac495 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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"); } diff --git a/src/util/fixed.rs b/src/util/fixed.rs deleted file mode 100644 index f7dce39..0000000 --- a/src/util/fixed.rs +++ /dev/null @@ -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, -} - -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 = 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 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 = 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 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(()) - } -} diff --git a/src/util/fixed/conversion.rs b/src/util/fixed/conversion.rs new file mode 100644 index 0000000..c613853 --- /dev/null +++ b/src/util/fixed/conversion.rs @@ -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 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 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) + } +} diff --git a/src/util/fixed/mod.rs b/src/util/fixed/mod.rs new file mode 100644 index 0000000..f201587 --- /dev/null +++ b/src/util/fixed/mod.rs @@ -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, +} + +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 = 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(()) + } +} diff --git a/src/util/fixed/op.rs b/src/util/fixed/op.rs new file mode 100644 index 0000000..ccbabb2 --- /dev/null +++ b/src/util/fixed/op.rs @@ -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 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 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 = 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) +} diff --git a/src/util/fixed/test.rs b/src/util/fixed/test.rs new file mode 100644 index 0000000..7615b72 --- /dev/null +++ b/src/util/fixed/test.rs @@ -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); +}