use crate::core::PackedArrayCore;
use crate::error::ArrayError;
use log::debug;
use serde::de::{Deserialize, Deserializer, SeqAccess, Visitor};
use serde::ser::{Serialize, SerializeSeq, Serializer};
use std::{fmt, mem};
pub const FLOAT16: (usize, usize) = (5, 10);
pub const BFLOAT16: (usize, usize) = (8, 7);
pub const FLOAT32: (usize, usize) = (8, 23);
pub const FLOAT64: (usize, usize) = (11, 52);
#[derive(Clone, Debug, PartialEq)]
pub struct FloatArray {
exp_bits: usize,
man_bits: usize,
total: usize,
bpu: usize,
bias: u64,
exp_max: u64,
length: usize,
data: Vec<u64>,
}
pub struct FloatIter<'a> {
arr: &'a FloatArray,
idx: usize,
len: usize,
}
fn validate_params(exp_bits: usize, man_bits: usize) -> Result<(), ArrayError> {
if !(2..=11).contains(&exp_bits)
|| !(1..=52).contains(&man_bits)
|| 1 + exp_bits + man_bits > 64
{
return Err(ArrayError::InvalidRange);
}
Ok(())
}
fn encode_float(v: f64, exp_bits: usize, man_bits: usize, bias: u64, exp_max: u64) -> u64 {
let bits = v.to_bits();
let sign = bits >> 63;
let f64_exp = (bits >> 52) & 0x7FF;
let f64_man = bits & ((1u64 << 52) - 1);
if f64_exp == 0x7FF {
let custom_man = if f64_man == 0 { 0u64 } else { 1u64 };
(sign << (exp_bits + man_bits)) | (exp_max << man_bits) | custom_man
} else if f64_exp == 0 {
sign << (exp_bits + man_bits)
} else {
let exp_biased = f64_exp as i64 - 1023 + bias as i64;
if exp_biased >= exp_max as i64 {
(sign << (exp_bits + man_bits)) | (exp_max << man_bits)
} else if exp_biased <= 0 {
sign << (exp_bits + man_bits)
} else {
let custom_exp = exp_biased as u64;
let custom_man = f64_man >> (52 - man_bits);
(sign << (exp_bits + man_bits)) | (custom_exp << man_bits) | custom_man
}
}
}
fn decode_float(raw: u64, exp_bits: usize, man_bits: usize, bias: u64, exp_max: u64) -> f64 {
let sign = raw >> (exp_bits + man_bits);
let custom_exp = (raw >> man_bits) & exp_max;
let custom_man = raw & ((1u64 << man_bits) - 1);
if custom_exp == exp_max {
let f64_man = if custom_man == 0 { 0u64 } else { 1u64 << 51 };
f64::from_bits((sign << 63) | (0x7FFu64 << 52) | f64_man)
} else if custom_exp == 0 {
f64::from_bits(sign << 63)
} else {
let f64_exp = custom_exp + 1023 - bias;
let f64_man = custom_man << (52 - man_bits);
f64::from_bits((sign << 63) | (f64_exp << 52) | f64_man)
}
}
impl FloatArray {
fn word_mask(&self) -> u64 {
if self.total == 64 {
u64::MAX
} else {
(1u64 << self.total) - 1
}
}
fn raw_get_encoded(&self, i: usize) -> u64 {
let wi = i / self.bpu;
let bit_offset = (i % self.bpu) * self.total;
(self.data[wi] >> bit_offset) & self.word_mask()
}
fn raw_set_encoded(&mut self, i: usize, encoded: u64) {
let wi = i / self.bpu;
let bit_offset = (i % self.bpu) * self.total;
let mask = self.word_mask();
self.data[wi] = (self.data[wi] & !(mask << bit_offset)) | (encoded << bit_offset);
}
fn resize(&mut self, len: usize) {
self.length = len;
self.data.resize(len.div_ceil(self.bpu), 0);
}
pub fn new(exp_bits: usize, man_bits: usize, len: usize) -> Result<Self, ArrayError> {
validate_params(exp_bits, man_bits)?;
let total = 1 + exp_bits + man_bits;
let bpu = 64 / total;
let bias = (1u64 << (exp_bits - 1)) - 1;
let exp_max = (1u64 << exp_bits) - 1;
let word_count = len.div_ceil(bpu);
debug!(
"FloatArray::new exp_bits={} man_bits={} total={} bpu={} bias={} exp_max={}",
exp_bits, man_bits, total, bpu, bias, exp_max
);
Ok(FloatArray {
exp_bits,
man_bits,
total,
bpu,
bias,
exp_max,
length: len,
data: vec![0u64; word_count],
})
}
pub fn new_with_vec(
exp_bits: usize,
man_bits: usize,
vals: Vec<f64>,
) -> Result<Self, ArrayError> {
let mut arr = Self::new(exp_bits, man_bits, 0)?;
arr.extend(vals)?;
Ok(arr)
}
pub fn new_with_iter(
exp_bits: usize,
man_bits: usize,
vals: impl IntoIterator<Item = f64>,
) -> Result<Self, ArrayError> {
let mut arr = Self::new(exp_bits, man_bits, 0)?;
arr.extend(vals)?;
Ok(arr)
}
pub fn get(&self, i: usize) -> Result<f64, ArrayError> {
if i >= self.length {
return Err(ArrayError::OutOfBounds);
}
let raw = self.raw_get_encoded(i);
Ok(decode_float(
raw,
self.exp_bits,
self.man_bits,
self.bias,
self.exp_max,
))
}
pub fn set(&mut self, i: usize, v: f64) -> Result<(), ArrayError> {
if i >= self.length {
return Err(ArrayError::OutOfBounds);
}
let encoded = encode_float(v, self.exp_bits, self.man_bits, self.bias, self.exp_max);
self.raw_set_encoded(i, encoded);
Ok(())
}
pub fn push(&mut self, v: f64) -> Result<usize, ArrayError> {
PackedArrayCore::core_push(self, v)
}
pub fn pop(&mut self) -> Result<f64, ArrayError> {
PackedArrayCore::core_pop(self)
}
pub fn extend(&mut self, vals: impl IntoIterator<Item = f64>) -> Result<(), ArrayError> {
PackedArrayCore::core_extend(self, vals)
}
pub fn extend_array(&mut self, other: &FloatArray) -> Result<(), ArrayError> {
if self.exp_bits == other.exp_bits
&& self.man_bits == other.man_bits
&& self.length.is_multiple_of(self.bpu)
{
debug!("fast path: total={}, length={}", self.total, self.length);
self.length += other.length;
self.data.extend_from_slice(&other.data);
return Ok(());
}
debug!("slow path: total={}, length={}", self.total, self.length);
self.extend(other.iter())
}
pub fn len(&self) -> usize {
self.length
}
pub fn is_empty(&self) -> bool {
self.length == 0
}
pub fn exp_bits(&self) -> usize {
self.exp_bits
}
pub fn man_bits(&self) -> usize {
self.man_bits
}
pub fn capacity(&self) -> usize {
self.data.len() * self.bpu
}
pub fn datasize(&self) -> usize {
mem::size_of::<FloatArray>() + mem::size_of::<u64>() * self.data.capacity()
}
pub fn iter(&self) -> FloatIter<'_> {
FloatIter {
arr: self,
idx: 0,
len: self.length,
}
}
pub fn sum(&self) -> Option<f64> {
if self.length == 0 {
return None;
}
Some(self.iter().fold(0.0f64, |acc, v| acc + v))
}
pub fn min(&self) -> Option<f64> {
let mut it = self.iter();
let first = it.next()?;
Some(it.fold(first, f64::min))
}
pub fn max(&self) -> Option<f64> {
let mut it = self.iter();
let first = it.next()?;
Some(it.fold(first, f64::max))
}
pub fn average(&self) -> Option<f64> {
if self.length == 0 {
return None;
}
Some(self.sum().unwrap() / self.length as f64)
}
}
impl PackedArrayCore for FloatArray {
type Item = f64;
fn raw_get(&self, i: usize) -> Result<f64, ArrayError> {
self.get(i)
}
fn raw_set(&mut self, i: usize, v: f64) -> Result<(), ArrayError> {
self.set(i, v)
}
fn raw_len(&self) -> usize {
self.length
}
fn raw_resize(&mut self, len: usize) {
self.resize(len)
}
}
impl<'a> Iterator for FloatIter<'a> {
type Item = f64;
fn next(&mut self) -> Option<f64> {
if self.idx >= self.len {
return None;
}
let v = self.arr.get(self.idx).unwrap();
self.idx += 1;
Some(v)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.len - self.idx;
(remaining, Some(remaining))
}
}
impl ExactSizeIterator for FloatIter<'_> {}
impl fmt::Display for FloatArray {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[e{}m{}][{}]=", self.exp_bits, self.man_bits, self.length)?;
let s = self
.iter()
.map(|x| x.to_string())
.collect::<Vec<_>>()
.join(",");
write!(f, "{}", s)
}
}
impl Serialize for FloatArray {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_seq(Some(self.length))?;
for v in self.iter() {
seq.serialize_element(&v)?;
}
seq.end()
}
}
struct FloatArrayVisitor;
impl<'de> Visitor<'de> for FloatArrayVisitor {
type Value = FloatArray;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "a sequence of floating-point numbers")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let (exp_bits, man_bits) = FLOAT64;
let mut arr = FloatArray::new(exp_bits, man_bits, 0)
.map_err(|e| serde::de::Error::custom(e.to_string()))?;
while let Some(v) = seq.next_element::<f64>()? {
arr.push(v)
.map_err(|e| serde::de::Error::custom(e.to_string()))?;
}
Ok(arr)
}
}
impl<'de> Deserialize<'de> for FloatArray {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_seq(FloatArrayVisitor)
}
}
#[cfg(test)]
mod tests;