use half::f16; use num_traits::{float::FloatCore, PrimInt, Unsigned}; pub fn bits(word: T, hi: usize, lo: usize) -> T where T: PrimInt + Unsigned { assert!(hi >= lo); let width = hi - lo + 1; (word >> lo) & ((T::one() << width) - T::one()) } pub fn nth(val: u32, pos: usize) -> u32 { (val >> (31 - pos as u32)) & 1 } pub fn f16_lo(val: u32) -> f16 { f16::from_bits((val & 0xffff) as u16) } pub fn f16_hi(val: u32) -> f16 { f16::from_bits(((val >> 16) & 0xffff) as u16) } pub fn sign_ext(num: u64, bits: usize) -> i64 { let mut value = num; let is_negative = (value >> (bits - 1)) & 1 != 0; if is_negative { value |= !0 << bits; } value as i64 } pub trait IEEEClass { fn exponent(&self) -> T; } impl IEEEClass for f32 { fn exponent(&self) -> u32 { (self.to_bits() & 0b01111111100000000000000000000000) >> 23 } } impl IEEEClass for f16 { fn exponent(&self) -> u16 { (self.to_bits() & 0b0111110000000000) >> 10 } } impl IEEEClass for f64 { fn exponent(&self) -> u64 { (self.to_bits() & 0b0111111111110000000000000000000000000000000000000000000000000000) >> 52 } } pub trait VOPModifier { fn negate(&self, pos: usize, modifier: usize) -> T; fn absolute(&self, pos: usize, modifier: usize) -> T; } impl VOPModifier for T where T: FloatCore, { fn negate(&self, pos: usize, modifier: usize) -> T { match (modifier >> pos) & 1 { 1 => -*self, _ => *self, } } fn absolute(&self, pos: usize, modifier: usize) -> T { match (modifier >> pos) & 1 { 1 => self.abs(), _ => *self, } } } pub fn extract_mantissa(x: f64) -> f64 { if x.is_infinite() || x.is_nan() { return x; } let bits = x.to_bits(); let mantissa_mask: u64 = 0x000FFFFFFFFFFFFF; let bias: u64 = 1023; let normalized_mantissa_bits = (bits & mantissa_mask) | ((bias - 1) << 52); return f64::from_bits(normalized_mantissa_bits); } pub fn ldexp(x: f64, exp: i32) -> f64 { x * 2f64.powi(exp) } #[cfg(test)] mod tests { use super::*; #[test] fn test_extract_mantissa() { assert_eq!(extract_mantissa(2.0f64), 0.5); } #[test] fn test_normal_exponent() { assert_eq!(2.5f32.exponent(), 128); assert_eq!(1.17549435e-38f32.exponent(), 1); assert_eq!(f32::INFINITY.exponent(), 255); assert_eq!(f32::NEG_INFINITY.exponent(), 255); } #[test] fn test_denormal_exponent() { assert_eq!(1.0e-40f32.exponent(), 0); assert_eq!(1.0e-42f32.exponent(), 0); assert_eq!(1.0e-44f32.exponent(), 0); assert_eq!((1.17549435e-38f32 / 2.0).exponent(), 0); } #[test] fn test_normal_exponent_f16() { assert_eq!(f16::from_f32(3.14f32).exponent(), 16); assert_eq!(f16::NEG_INFINITY.exponent(), 31); assert_eq!(f16::INFINITY.exponent(), 31); } #[test] fn test_neg() { assert_eq!(0.3_f32.negate(0, 0b001), -0.3_f32); assert_eq!(0.3_f32.negate(1, 0b010), -0.3_f32); assert_eq!(0.3_f32.negate(2, 0b100), -0.3_f32); assert_eq!(0.3_f32.negate(0, 0b110), 0.3_f32); assert_eq!(0.3_f32.negate(1, 0b010), -0.3_f32); assert_eq!(0.0_f32.negate(0, 0b001).to_bits(), (-0.0f32).to_bits()); assert_eq!((-0.0_f32).negate(0, 0b001).to_bits(), 0); } #[test] fn test_sign_ext() { assert_eq!(sign_ext(0b000000000000000101000, 21), 40); assert_eq!(sign_ext(0b111111111111111011000, 21), -40); assert_eq!(sign_ext(0b000000000000000000000, 21), 0); assert_eq!(sign_ext(0b111111111111111111111, 21), -1); assert_eq!(sign_ext(0b111000000000000000000, 21), -262144); assert_eq!(sign_ext(0b000111111111111111111, 21), 262143); assert_eq!(sign_ext(7608, 13), -584); } } use std::sync::LazyLock; pub static DEBUG: LazyLock = LazyLock::new(|| std::env::var("DEBUG").map(|v| v.parse::().unwrap_or(0) >= 6).unwrap_or(false)); pub fn colored(st:&str, color:&str) -> String { let ansi_code = match color { "green" => format!("\x1b[{};2;39;176;139m", 38), "gray" => format!("\x1b[{};2;169;169;169m", 38), _ => format!("\x1b[{};2;255;255;255m", 38), }; format!("{}{}{}", ansi_code, st, "\x1b[0m") } #[macro_export] macro_rules! todo_instr { ($x:expr) => {{ println!("{:08X}", $x); Err(1) }}; }