use std::ptr::read_unaligned;
pub const MAX_EXPONENT_F64: u8 = 18;
pub const MAX_EXPONENT_F32: u8 = 10;
pub const MAGIC_NUMBER_F64: f64 = 6755399441055744.0; pub const MAGIC_NUMBER_F32: f32 = 12582912.0;
pub const ENCODING_UPPER_LIMIT_F64: f64 = 9223372036854774784.0;
pub const ENCODING_UPPER_LIMIT_F32: f32 = 2147483520.0;
pub const EXP_ARR_F64: [f64; 19] = [
1.0,
10.0,
100.0,
1_000.0,
10_000.0,
100_000.0,
1_000_000.0,
10_000_000.0,
100_000_000.0,
1_000_000_000.0,
10_000_000_000.0,
100_000_000_000.0,
1_000_000_000_000.0,
10_000_000_000_000.0,
100_000_000_000_000.0,
1_000_000_000_000_000.0,
10_000_000_000_000_000.0,
100_000_000_000_000_000.0,
1_000_000_000_000_000_000.0,
];
pub const FRAC_ARR_F64: [f64; 19] = [
1.0,
0.1,
0.01,
0.001,
0.0001,
0.00001,
0.000001,
0.0000001,
0.00000001,
0.000000001,
0.0000000001,
0.00000000001,
0.000000000001,
0.0000000000001,
0.00000000000001,
0.000000000000001,
0.0000000000000001,
0.00000000000000001,
0.000000000000000001,
];
pub const FACT_ARR_F64: [i64; 19] = [
1,
10,
100,
1_000,
10_000,
100_000,
1_000_000,
10_000_000,
100_000_000,
1_000_000_000,
10_000_000_000,
100_000_000_000,
1_000_000_000_000,
10_000_000_000_000,
100_000_000_000_000,
1_000_000_000_000_000,
10_000_000_000_000_000,
100_000_000_000_000_000,
1_000_000_000_000_000_000,
];
pub const EXP_ARR_F32: [f32; 11] = [
1.0,
10.0,
100.0,
1_000.0,
10_000.0,
100_000.0,
1_000_000.0,
10_000_000.0,
100_000_000.0,
1_000_000_000.0,
10_000_000_000.0,
];
pub const FRAC_ARR_F32: [f32; 11] = [
1.0,
0.1,
0.01,
0.001,
0.0001,
0.00001,
0.000001,
0.0000001,
0.00000001,
0.000000001,
0.0000000001,
];
pub const FACT_ARR_F32: [i64; 11] = [
1,
10,
100,
1_000,
10_000,
100_000,
1_000_000,
10_000_000,
100_000_000,
1_000_000_000,
10_000_000_000,
];
#[inline(always)]
pub const fn bits_needed(max_val: u64) -> u8 {
(64 - max_val.leading_zeros()) as u8
}
#[inline(always)]
pub const fn bit_mask(bit_width: u8) -> u64 {
if bit_width >= 64 {
u64::MAX
} else {
(1u64 << bit_width) - 1
}
}
#[inline(always)]
pub const fn bits_needed_u64(max_val: u64) -> u8 {
bits_needed(max_val)
}
#[inline(always)]
pub const fn bit_mask_u64(bit_width: u8) -> u64 {
bit_mask(bit_width)
}
pub trait AlpFloat: Copy + Default + PartialEq + PartialOrd + Send + Sync + 'static {
type Int: Copy + Default + PartialEq + Eq + PartialOrd + Ord + Send + Sync + 'static;
type RawBits: Copy + Default + Send + Sync + 'static;
const TYPE_BYTE: u8;
const MAX_EXPONENT: u8;
const MAX_FAC: u8;
const MAX_BIT_WIDTH: u8;
const MAGIC_NUMBER: Self;
const ENCODING_UPPER_LIMIT: Self;
const EXCEPTION_PENALTY: usize;
const EXC_ENTRY_SIZE: usize;
const BASE_SIZE: usize;
const ZERO: Self;
const ZERO_INT: Self::Int;
const MIN_INT: Self::Int;
const MAX_INT: Self::Int;
fn exp_factor(exp: u8, fac: u8) -> Self;
fn fac_int(fac: u8) -> i64;
fn frac_exp(exp: u8) -> Self;
fn is_impossible(self) -> bool;
fn try_encode_fast(self, exp_factor: Self, fac_int: i64, frac_exp: Self) -> Option<Self::Int>;
fn fast_round_to_int(self, exp_factor: Self) -> Self::Int;
fn decode_from_int(encoded: Self::Int, fac_int: i64, frac_exp: Self) -> Self;
fn decode_from_offset(offset: u64, base: Self::Int, fac_int: i64, frac_exp: Self) -> Self;
fn int_diff_to_u64(val: Self::Int, base: Self::Int) -> u64;
fn u64_to_int_add(offset: u64, base: Self::Int) -> Self::Int;
fn calc_range(min_val: Self::Int, max_val: Self::Int) -> u64;
fn bits_needed(max_offset: u64) -> u8;
fn to_raw_bits(self) -> Self::RawBits;
fn from_raw_bits(bits: Self::RawBits) -> Self;
fn write_base(base: Self::Int, dst: &mut Vec<u8>);
fn read_base(src: &[u8]) -> Self::Int;
fn write_exception(pos: u16, bits: Self::RawBits, dst: &mut Vec<u8>);
fn read_exception(chunk: &[u8]) -> (usize, Self);
#[inline(always)]
fn build_lut<const N: usize>(base: Self::Int, fac_int: i64, frac_exp: Self) -> [Self; N] {
let mut lut = [Self::ZERO; N];
for (i, slot) in lut.iter_mut().enumerate() {
*slot = Self::decode_from_offset(i as u64, base, fac_int, frac_exp);
}
lut
}
}
impl AlpFloat for f64 {
type Int = i64;
type RawBits = u64;
const TYPE_BYTE: u8 = 1;
const MAX_EXPONENT: u8 = MAX_EXPONENT_F64;
const MAX_FAC: u8 = 8;
const MAX_BIT_WIDTH: u8 = 64;
const MAGIC_NUMBER: Self = MAGIC_NUMBER_F64;
const ENCODING_UPPER_LIMIT: Self = ENCODING_UPPER_LIMIT_F64;
const EXCEPTION_PENALTY: usize = 80;
const EXC_ENTRY_SIZE: usize = 10;
const BASE_SIZE: usize = 8;
const ZERO: Self = 0.0;
const ZERO_INT: Self::Int = 0;
const MIN_INT: Self::Int = i64::MIN;
const MAX_INT: Self::Int = i64::MAX;
#[inline(always)]
fn exp_factor(exp: u8, fac: u8) -> Self {
unsafe { *EXP_ARR_F64.get_unchecked((exp - fac) as usize) }
}
#[inline(always)]
fn fac_int(fac: u8) -> i64 {
unsafe { *FACT_ARR_F64.get_unchecked(fac as usize) }
}
#[inline(always)]
fn frac_exp(exp: u8) -> Self {
unsafe { *FRAC_ARR_F64.get_unchecked(exp as usize) }
}
#[inline(always)]
fn is_impossible(self) -> bool {
!self.is_finite()
|| self.abs() > ENCODING_UPPER_LIMIT_F64
|| (self == 0.0 && self.is_sign_negative())
}
#[inline(always)]
fn try_encode_fast(self, exp_factor: Self, fac_int: i64, frac_exp: Self) -> Option<Self::Int> {
if self.is_impossible() {
return None;
}
let scaled = self * exp_factor;
if scaled.is_impossible() {
return None;
}
let rounded = (scaled + MAGIC_NUMBER_F64) - MAGIC_NUMBER_F64;
let encoded = rounded as i64;
let int_with_fac = if fac_int == 1 {
encoded
} else {
encoded.checked_mul(fac_int)?
};
let decoded = (int_with_fac as f64) * frac_exp;
if decoded.to_bits() == self.to_bits() {
Some(encoded)
} else {
None
}
}
#[inline(always)]
fn fast_round_to_int(self, exp_factor: Self) -> Self::Int {
let scaled = self * exp_factor;
let rounded = (scaled + MAGIC_NUMBER_F64) - MAGIC_NUMBER_F64;
rounded as i64
}
#[inline(always)]
fn decode_from_int(encoded: Self::Int, fac_int: i64, frac_exp: Self) -> Self {
let int_with_fac = if fac_int == 1 {
encoded
} else {
encoded.wrapping_mul(fac_int)
};
(int_with_fac as f64) * frac_exp
}
#[inline(always)]
fn decode_from_offset(offset: u64, base: Self::Int, fac_int: i64, frac_exp: Self) -> Self {
let unscaled = (offset as i64).wrapping_add(base);
let int_with_fac = if fac_int == 1 {
unscaled
} else {
unscaled.wrapping_mul(fac_int)
};
(int_with_fac as f64) * frac_exp
}
#[inline(always)]
fn int_diff_to_u64(val: Self::Int, base: Self::Int) -> u64 {
val.wrapping_sub(base) as u64
}
#[inline(always)]
fn u64_to_int_add(offset: u64, base: Self::Int) -> Self::Int {
(offset as i64).wrapping_add(base)
}
#[inline(always)]
fn calc_range(min_val: Self::Int, max_val: Self::Int) -> u64 {
max_val.wrapping_sub(min_val) as u64
}
#[inline(always)]
fn bits_needed(max_offset: u64) -> u8 {
bits_needed_u64(max_offset)
}
#[inline(always)]
fn to_raw_bits(self) -> Self::RawBits {
self.to_bits()
}
#[inline(always)]
fn from_raw_bits(bits: Self::RawBits) -> Self {
f64::from_bits(bits)
}
#[inline(always)]
fn write_base(base: Self::Int, dst: &mut Vec<u8>) {
dst.extend_from_slice(&base.to_le_bytes());
}
#[inline(always)]
fn read_base(src: &[u8]) -> Self::Int {
unsafe {
let bytes = read_unaligned(src.as_ptr().cast::<[u8; 8]>());
i64::from_le_bytes(bytes)
}
}
#[inline(always)]
fn write_exception(pos: u16, bits: Self::RawBits, dst: &mut Vec<u8>) {
dst.extend_from_slice(&pos.to_le_bytes());
dst.extend_from_slice(&bits.to_le_bytes());
}
#[inline(always)]
fn read_exception(chunk: &[u8]) -> (usize, Self) {
unsafe {
let pos_bytes = read_unaligned(chunk.as_ptr().cast::<[u8; 2]>());
let pos = u16::from_le_bytes(pos_bytes) as usize;
let bits_bytes = read_unaligned(chunk.as_ptr().add(2).cast::<[u8; 8]>());
let bits = u64::from_le_bytes(bits_bytes);
(pos, f64::from_bits(bits))
}
}
}
impl AlpFloat for f32 {
type Int = i32;
type RawBits = u32;
const TYPE_BYTE: u8 = 2;
const MAX_EXPONENT: u8 = MAX_EXPONENT_F32;
const MAX_FAC: u8 = 4;
const MAX_BIT_WIDTH: u8 = 32;
const MAGIC_NUMBER: Self = MAGIC_NUMBER_F32;
const ENCODING_UPPER_LIMIT: Self = ENCODING_UPPER_LIMIT_F32;
const EXCEPTION_PENALTY: usize = 48;
const EXC_ENTRY_SIZE: usize = 6;
const BASE_SIZE: usize = 4;
const ZERO: Self = 0.0;
const ZERO_INT: Self::Int = 0;
const MIN_INT: Self::Int = i32::MIN;
const MAX_INT: Self::Int = i32::MAX;
#[inline(always)]
fn exp_factor(exp: u8, fac: u8) -> Self {
unsafe { *EXP_ARR_F32.get_unchecked((exp - fac) as usize) }
}
#[inline(always)]
fn fac_int(fac: u8) -> i64 {
unsafe { *FACT_ARR_F32.get_unchecked(fac as usize) }
}
#[inline(always)]
fn frac_exp(exp: u8) -> Self {
unsafe { *FRAC_ARR_F32.get_unchecked(exp as usize) }
}
#[inline(always)]
fn is_impossible(self) -> bool {
!self.is_finite()
|| self.abs() > ENCODING_UPPER_LIMIT_F32
|| (self == 0.0 && self.is_sign_negative())
}
#[inline(always)]
fn try_encode_fast(self, exp_factor: Self, fac_int: i64, frac_exp: Self) -> Option<Self::Int> {
if self.is_impossible() {
return None;
}
let scaled = self * exp_factor;
if scaled.is_impossible() {
return None;
}
let rounded = (scaled + MAGIC_NUMBER_F32) - MAGIC_NUMBER_F32;
let encoded = rounded as i32;
let int_with_fac = if fac_int == 1 {
encoded as i64
} else {
(encoded as i64).checked_mul(fac_int)?
};
let decoded = (int_with_fac as f32) * frac_exp;
if decoded.to_bits() == self.to_bits() {
Some(encoded)
} else {
None
}
}
#[inline(always)]
fn fast_round_to_int(self, exp_factor: Self) -> Self::Int {
let scaled = self * exp_factor;
let rounded = (scaled + MAGIC_NUMBER_F32) - MAGIC_NUMBER_F32;
rounded as i32
}
#[inline(always)]
fn decode_from_int(encoded: Self::Int, fac_int: i64, frac_exp: Self) -> Self {
let int_with_fac = if fac_int == 1 {
encoded as i64
} else {
(encoded as i64).wrapping_mul(fac_int)
};
(int_with_fac as f32) * frac_exp
}
#[inline(always)]
fn decode_from_offset(offset: u64, base: Self::Int, fac_int: i64, frac_exp: Self) -> Self {
let unscaled = (offset as i32).wrapping_add(base);
let int_with_fac = if fac_int == 1 {
unscaled as i64
} else {
(unscaled as i64).wrapping_mul(fac_int)
};
(int_with_fac as f32) * frac_exp
}
#[inline(always)]
fn int_diff_to_u64(val: Self::Int, base: Self::Int) -> u64 {
val.wrapping_sub(base) as u32 as u64
}
#[inline(always)]
fn u64_to_int_add(offset: u64, base: Self::Int) -> Self::Int {
(offset as i32).wrapping_add(base)
}
#[inline(always)]
fn calc_range(min_val: Self::Int, max_val: Self::Int) -> u64 {
max_val.wrapping_sub(min_val) as u32 as u64
}
#[inline(always)]
fn bits_needed(max_offset: u64) -> u8 {
bits_needed(max_offset)
}
#[inline(always)]
fn to_raw_bits(self) -> Self::RawBits {
self.to_bits()
}
#[inline(always)]
fn from_raw_bits(bits: Self::RawBits) -> Self {
f32::from_bits(bits)
}
#[inline(always)]
fn write_base(base: Self::Int, dst: &mut Vec<u8>) {
dst.extend_from_slice(&base.to_le_bytes());
}
#[inline(always)]
fn read_base(src: &[u8]) -> Self::Int {
unsafe {
let bytes = read_unaligned(src.as_ptr().cast::<[u8; 4]>());
i32::from_le_bytes(bytes)
}
}
#[inline(always)]
fn write_exception(pos: u16, bits: Self::RawBits, dst: &mut Vec<u8>) {
dst.extend_from_slice(&pos.to_le_bytes());
dst.extend_from_slice(&bits.to_le_bytes());
}
#[inline(always)]
fn read_exception(chunk: &[u8]) -> (usize, Self) {
unsafe {
let pos_bytes = read_unaligned(chunk.as_ptr().cast::<[u8; 2]>());
let pos = u16::from_le_bytes(pos_bytes) as usize;
let bits_bytes = read_unaligned(chunk.as_ptr().add(2).cast::<[u8; 4]>());
let bits = u32::from_le_bytes(bits_bytes);
(pos, f32::from_bits(bits))
}
}
}