#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Precision {
F64,
F32,
F16,
Bf16,
}
pub struct AutoPrecision {
pub preferred: Precision,
pub allow_downcast: bool,
pub minimum: Precision,
}
impl AutoPrecision {
pub fn new() -> Self {
Self {
preferred: Precision::F32,
allow_downcast: true,
minimum: Precision::F32,
}
}
pub fn select(&self, supports_f16: bool, supports_bf16: bool) -> Precision {
if self.allow_downcast {
match self.preferred {
Precision::F64 => Precision::F64,
Precision::F32 => Precision::F32,
Precision::F16 => {
if supports_f16 { Precision::F16 }
else if supports_bf16 { Precision::Bf16 }
else { Precision::F32 }
}
Precision::Bf16 => {
if supports_bf16 { Precision::Bf16 }
else if supports_f16 { Precision::F16 }
else { Precision::F32 }
}
}
} else {
self.preferred
}
}
}
pub fn check_f16_support() -> (bool, bool) {
#[cfg(target_arch = "x86_64")]
{
let mut f16c = false;
let mut bf16 = false;
#[cfg(target_feature = "f16c")]
{ f16c = true; }
#[cfg(target_feature = "avx512bf16")]
{ bf16 = true; }
(f16c, bf16)
}
#[cfg(target_arch = "aarch64")]
{
(true, false)
}
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
{
(false, false)
}
}
pub fn f32_to_f16(src: &[f32]) -> Vec<u16> {
#[cfg(feature = "half")]
{
src.iter().map(|&x| half::f16::from_f32(x).to_bits()).collect()
}
#[cfg(not(feature = "half"))]
{
src.iter().map(|&x| {
let bits = x.to_bits();
(bits >> 16) as u16
}).collect()
}
}
pub fn f16_to_f32(src: &[u16]) -> Vec<f32> {
#[cfg(feature = "half")]
{
src.iter().map(|&x| half::f16::from_bits(x).to_f32()).collect()
}
#[cfg(not(feature = "half"))]
{
src.iter().map(|&x| f32::from_bits((x as u32) << 16)).collect()
}
}