#![no_std]
#![deny(missing_docs, unsafe_op_in_unsafe_fn)]
mod ops;
#[cfg(all(
feature = "simd",
target_arch = "x86_64",
not(target_feature = "avx512f"),
not(any(miri, target_os = "none", target_os = "uefi", target_env = "sgx"))
))]
mod simd;
use core::fmt;
#[allow(non_camel_case_types)]
#[repr(transparent)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct f8(u8);
impl f8 {
pub const ZERO: Self = Self(0);
pub const ONE: Self = Self(255);
pub const MIN: Self = Self::ZERO;
pub const MAX: Self = Self::ONE;
#[inline]
pub const fn from_bits(bits: u8) -> Self {
Self(bits)
}
#[inline]
pub const fn to_bits(self) -> u8 {
self.0
}
#[inline]
pub const fn from_f32(value: f32) -> Self {
let bits = value.to_bits();
if bits > 0x7f80_0000 || bits < 0x3b00_0000 {
return Self::ZERO;
}
if bits >= 0x3f80_0000 {
return Self::ONE;
}
let product = ((bits & 0x007f_ffff) | 0x0080_0000) * 255;
let shift = 149 - (bits >> 23); let guard = product >> shift;
let sticky = product & ((1 << shift) - 1);
let lower = guard >> 1;
Self((lower + ((guard & 1) & ((sticky != 0 || lower & 1 != 0) as u32))) as u8)
}
#[inline]
pub const fn to_f32(self) -> f32 {
if self.0 == 0 {
return 0.0;
}
let repeated = self.0 as u32 * 0x0101_0101;
let zeros = repeated.leading_zeros();
let normalized = repeated << zeros;
let significand = (normalized >> 8) + ((normalized >> 7) & 1);
f32::from_bits(((125 - zeros) << 23) + significand)
}
#[inline]
pub fn from_f32_slice(src: &[f32], dst: &mut [Self]) {
assert_eq!(
src.len(),
dst.len(),
"source and destination lengths differ"
);
#[cfg(all(
feature = "simd",
target_arch = "x86_64",
not(target_feature = "avx512f"),
not(any(miri, target_os = "none", target_os = "uefi", target_env = "sgx"))
))]
if src.len() >= 32 && simd::available() {
unsafe { simd::encode(src, dst) };
return;
}
for (&value, out) in src.iter().zip(dst) {
*out = Self::from_f32(value);
}
}
#[inline]
pub fn to_f32_slice(src: &[Self], dst: &mut [f32]) {
assert_eq!(
src.len(),
dst.len(),
"source and destination lengths differ"
);
for (&value, out) in src.iter().zip(dst) {
#[cfg(any(
target_feature = "sse2",
target_feature = "neon",
target_feature = "vfp2",
target_feature = "f",
target_family = "wasm"
))]
{
*out = value.0 as f32 / 255.0;
}
#[cfg(not(any(
target_feature = "sse2",
target_feature = "neon",
target_feature = "vfp2",
target_feature = "f",
target_family = "wasm"
)))]
{
*out = value.to_f32();
}
}
}
#[inline]
pub fn as_bytes(values: &[Self]) -> &[u8] {
unsafe { core::slice::from_raw_parts(values.as_ptr().cast(), values.len()) }
}
#[inline]
pub fn as_bytes_mut(values: &mut [Self]) -> &mut [u8] {
unsafe { core::slice::from_raw_parts_mut(values.as_mut_ptr().cast(), values.len()) }
}
#[inline]
pub fn from_bytes(bytes: &[u8]) -> &[Self] {
unsafe { core::slice::from_raw_parts(bytes.as_ptr().cast(), bytes.len()) }
}
#[inline]
pub fn from_bytes_mut(bytes: &mut [u8]) -> &mut [Self] {
unsafe { core::slice::from_raw_parts_mut(bytes.as_mut_ptr().cast(), bytes.len()) }
}
}
impl From<u8> for f8 {
#[inline]
fn from(value: u8) -> Self {
Self::from_bits(value)
}
}
impl From<f32> for f8 {
#[inline]
fn from(value: f32) -> Self {
Self::from_f32(value)
}
}
impl From<f8> for u8 {
#[inline]
fn from(value: f8) -> Self {
value.to_bits()
}
}
impl From<f8> for f32 {
#[inline]
fn from(value: f8) -> Self {
value.to_f32()
}
}
impl fmt::Display for f8 {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.to_f32().fmt(f)
}
}