Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/rust/half/src/binary16/   (Firefox Browser Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 15 kB image not shown  

Quelle  convert.rs

  Sprache: Rust
 

#![allow(dead_code, unused_imports)]

macro_rules! convert_fn {
    (fn $name:ident($var:ident : $vartype:ty) -> $restype:ty {
            if feature("f16c") { $f16c:expr }
            else { $fallback:expr }}) => {
        #[inline]
        pub(cratefn $name($var: $vartype) -> $restype {
            // Use CPU feature detection if using std
            #[cfg(all(
                feature = "use-intrinsics",
                feature = "std",
                any(target_arch = "x86", target_arch = "x86_64"),
                not(target_feature = "f16c")
            ))]
            {
                if is_x86_feature_detected!("f16c") {
                    $f16c
                } else {
                    $fallback
                }
            }
            // Use intrinsics directly when a compile target or using no_std
            #[cfg(all(
                feature = "use-intrinsics",
                any(target_arch = "x86", target_arch = "x86_64"),
                target_feature = "f16c"
            ))]
            {
                $f16c
            }
            // Fallback to software
            #[cfg(any(
                not(feature = "use-intrinsics"),
                not(any(target_arch = "x86", target_arch = "x86_64")),
                all(not(feature = "std"), not(target_feature = "f16c"))
            ))]
            {
                $fallback
            }
        }
    };
}

convert_fn! {
    fn f32_to_f16(f: f32) -> u16 {
        if feature("f16c") {
            unsafe { x86::f32_to_f16_x86_f16c(f) }
        } else {
            f32_to_f16_fallback(f)
        }
    }
}

convert_fn! {
    fn f64_to_f16(f: f64) -> u16 {
        if feature("f16c") {
            unsafe { x86::f32_to_f16_x86_f16c(f as f32) }
        } else {
            f64_to_f16_fallback(f)
        }
    }
}

convert_fn! {
    fn f16_to_f32(i: u16) -> f32 {
        if feature("f16c") {
            unsafe { x86::f16_to_f32_x86_f16c(i) }
        } else {
            f16_to_f32_fallback(i)
        }
    }
}

convert_fn! {
    fn f16_to_f64(i: u16) -> f64 {
        if feature("f16c") {
            unsafe { x86::f16_to_f32_x86_f16c(i) as f64 }
        } else {
            f16_to_f64_fallback(i)
        }
    }
}

// TODO: While SIMD versions are faster, further improvements can be made by doing runtime feature
// detection once at beginning of convert slice method, rather than per chunk

convert_fn! {
    fn f32x4_to_f16x4(f: &[f32]) -> [u16; 4] {
        if feature("f16c") {
            unsafe { x86::f32x4_to_f16x4_x86_f16c(f) }
        } else {
            f32x4_to_f16x4_fallback(f)
        }
    }
}

convert_fn! {
    fn f16x4_to_f32x4(i: &[u16]) -> [f32; 4] {
        if feature("f16c") {
            unsafe { x86::f16x4_to_f32x4_x86_f16c(i) }
        } else {
            f16x4_to_f32x4_fallback(i)
        }
    }
}

convert_fn! {
    fn f64x4_to_f16x4(f: &[f64]) -> [u16; 4] {
        if feature("f16c") {
            unsafe { x86::f64x4_to_f16x4_x86_f16c(f) }
        } else {
            f64x4_to_f16x4_fallback(f)
        }
    }
}

convert_fn! {
    fn f16x4_to_f64x4(i: &[u16]) -> [f64; 4] {
        if feature("f16c") {
            unsafe { x86::f16x4_to_f64x4_x86_f16c(i) }
        } else {
            f16x4_to_f64x4_fallback(i)
        }
    }
}

/////////////// Fallbacks ////////////////

// In the below functions, round to nearest, with ties to even.
// Let us call the most significant bit that will be shifted out the round_bit.
//
// Round up if either
//  a) Removed part > tie.
//     (mantissa & round_bit) != 0 && (mantissa & (round_bit - 1)) != 0
//  b) Removed part == tie, and retained part is odd.
//     (mantissa & round_bit) != 0 && (mantissa & (2 * round_bit)) != 0
// (If removed part == tie and retained part is even, do not round up.)
// These two conditions can be combined into one:
//     (mantissa & round_bit) != 0 && (mantissa & ((round_bit - 1) | (2 * round_bit))) != 0
// which can be simplified into
//     (mantissa & round_bit) != 0 && (mantissa & (3 * round_bit - 1)) != 0

fn f32_to_f16_fallback(value: f32) -> u16 {
    // Convert to raw bytes
    let x = value.to_bits();

    // Extract IEEE754 components
    let sign = x & 0x8000_0000u32;
    let exp = x & 0x7F80_0000u32;
    let man = x & 0x007F_FFFFu32;

    // Check for all exponent bits being set, which is Infinity or NaN
    if exp == 0x7F80_0000u32 {
        // Set mantissa MSB for NaN (and also keep shifted mantissa bits)
        let nan_bit = if man == 0 { 0 } else { 0x0200u32 };
        return ((sign >> 16) | 0x7C00u32 | nan_bit | (man >> 13)) as u16;
    }

    // The number is normalized, start assembling half precision version
    let half_sign = sign >> 16;
    // Unbias the exponent, then bias for half precision
    let unbiased_exp = ((exp >> 23as i32) - 127;
    let half_exp = unbiased_exp + 15;

    // Check for exponent overflow, return +infinity
    if half_exp >= 0x1F {
        return (half_sign | 0x7C00u32) as u16;
    }

    // Check for underflow
    if half_exp <= 0 {
        // Check mantissa for what we can do
        if 14 - half_exp > 24 {
            // No rounding possibility, so this is a full underflow, return signed zero
            return half_sign as u16;
        }
        // Don't forget about hidden leading mantissa bit when assembling mantissa
        let man = man | 0x0080_0000u32;
        let mut half_man = man >> (14 - half_exp);
        // Check for rounding (see comment above functions)
        let round_bit = 1 << (13 - half_exp);
        if (man & round_bit) != 0 && (man & (3 * round_bit - 1)) != 0 {
            half_man += 1;
        }
        // No exponent for subnormals
        return (half_sign | half_man) as u16;
    }

    // Rebias the exponent
    let half_exp = (half_exp as u32) << 10;
    let half_man = man >> 13;
    // Check for rounding (see comment above functions)
    let round_bit = 0x0000_1000u32;
    if (man & round_bit) != 0 && (man & (3 * round_bit - 1)) != 0 {
        // Round it
        ((half_sign | half_exp | half_man) + 1as u16
    } else {
        (half_sign | half_exp | half_man) as u16
    }
}

fn f64_to_f16_fallback(value: f64) -> u16 {
    // Convert to raw bytes, truncating the last 32-bits of mantissa; that precision will always
    // be lost on half-precision.
    let val = value.to_bits();
    let x = (val >> 32as u32;

    // Extract IEEE754 components
    let sign = x & 0x8000_0000u32;
    let exp = x & 0x7FF0_0000u32;
    let man = x & 0x000F_FFFFu32;

    // Check for all exponent bits being set, which is Infinity or NaN
    if exp == 0x7FF0_0000u32 {
        // Set mantissa MSB for NaN (and also keep shifted mantissa bits).
        // We also have to check the last 32 bits.
        let nan_bit = if man == 0 && (val as u32 == 0) {
            0
        } else {
            0x0200u32
        };
        return ((sign >> 16) | 0x7C00u32 | nan_bit | (man >> 10)) as u16;
    }

    // The number is normalized, start assembling half precision version
    let half_sign = sign >> 16;
    // Unbias the exponent, then bias for half precision
    let unbiased_exp = ((exp >> 20as i64) - 1023;
    let half_exp = unbiased_exp + 15;

    // Check for exponent overflow, return +infinity
    if half_exp >= 0x1F {
        return (half_sign | 0x7C00u32) as u16;
    }

    // Check for underflow
    if half_exp <= 0 {
        // Check mantissa for what we can do
        if 10 - half_exp > 21 {
            // No rounding possibility, so this is a full underflow, return signed zero
            return half_sign as u16;
        }
        // Don't forget about hidden leading mantissa bit when assembling mantissa
        let man = man | 0x0010_0000u32;
        let mut half_man = man >> (11 - half_exp);
        // Check for rounding (see comment above functions)
        let round_bit = 1 << (10 - half_exp);
        if (man & round_bit) != 0 && (man & (3 * round_bit - 1)) != 0 {
            half_man += 1;
        }
        // No exponent for subnormals
        return (half_sign | half_man) as u16;
    }

    // Rebias the exponent
    let half_exp = (half_exp as u32) << 10;
    let half_man = man >> 10;
    // Check for rounding (see comment above functions)
    let round_bit = 0x0000_0200u32;
    if (man & round_bit) != 0 && (man & (3 * round_bit - 1)) != 0 {
        // Round it
        ((half_sign | half_exp | half_man) + 1as u16
    } else {
        (half_sign | half_exp | half_man) as u16
    }
}

fn f16_to_f32_fallback(i: u16) -> f32 {
    // Check for signed zero
    if i & 0x7FFFu16 == 0 {
        return f32::from_bits((i as u32) << 16);
    }

    let half_sign = (i & 0x8000u16) as u32;
    let half_exp = (i & 0x7C00u16) as u32;
    let half_man = (i & 0x03FFu16) as u32;

    // Check for an infinity or NaN when all exponent bits set
    if half_exp == 0x7C00u32 {
        // Check for signed infinity if mantissa is zero
        if half_man == 0 {
            return f32::from_bits((half_sign << 16) | 0x7F80_0000u32);
        } else {
            // NaN, keep current mantissa but also set most significiant mantissa bit
            return f32::from_bits((half_sign << 16) | 0x7FC0_0000u32 | (half_man << 13));
        }
    }

    // Calculate single-precision components with adjusted exponent
    let sign = half_sign << 16;
    // Unbias exponent
    let unbiased_exp = ((half_exp as i32) >> 10) - 15;

    // Check for subnormals, which will be normalized by adjusting exponent
    if half_exp == 0 {
        // Calculate how much to adjust the exponent by
        let e = (half_man as u16).leading_zeros() - 6;

        // Rebias and adjust exponent
        let exp = (127 - 15 - e) << 23;
        let man = (half_man << (14 + e)) & 0x7F_FF_FFu32;
        return f32::from_bits(sign | exp | man);
    }

    // Rebias exponent for a normalized normal
    let exp = ((unbiased_exp + 127as u32) << 23;
    let man = (half_man & 0x03FFu32) << 13;
    f32::from_bits(sign | exp | man)
}

fn f16_to_f64_fallback(i: u16) -> f64 {
    // Check for signed zero
    if i & 0x7FFFu16 == 0 {
        return f64::from_bits((i as u64) << 48);
    }

    let half_sign = (i & 0x8000u16) as u64;
    let half_exp = (i & 0x7C00u16) as u64;
    let half_man = (i & 0x03FFu16) as u64;

    // Check for an infinity or NaN when all exponent bits set
    if half_exp == 0x7C00u64 {
        // Check for signed infinity if mantissa is zero
        if half_man == 0 {
            return f64::from_bits((half_sign << 48) | 0x7FF0_0000_0000_0000u64);
        } else {
            // NaN, keep current mantissa but also set most significiant mantissa bit
            return f64::from_bits((half_sign << 48) | 0x7FF8_0000_0000_0000u64 | (half_man << 42));
        }
    }

    // Calculate double-precision components with adjusted exponent
    let sign = half_sign << 48;
    // Unbias exponent
    let unbiased_exp = ((half_exp as i64) >> 10) - 15;

    // Check for subnormals, which will be normalized by adjusting exponent
    if half_exp == 0 {
        // Calculate how much to adjust the exponent by
        let e = (half_man as u16).leading_zeros() - 6;

        // Rebias and adjust exponent
        let exp = ((1023 - 15 - e) as u64) << 52;
        let man = (half_man << (43 + e)) & 0xF_FFFF_FFFF_FFFFu64;
        return f64::from_bits(sign | exp | man);
    }

    // Rebias exponent for a normalized normal
    let exp = ((unbiased_exp + 1023as u64) << 52;
    let man = (half_man & 0x03FFu64) << 42;
    f64::from_bits(sign | exp | man)
}

#[inline]
fn f16x4_to_f32x4_fallback(v: &[u16]) -> [f32; 4] {
    debug_assert!(v.len() >= 4);

    [
        f16_to_f32_fallback(v[0]),
        f16_to_f32_fallback(v[1]),
        f16_to_f32_fallback(v[2]),
        f16_to_f32_fallback(v[3]),
    ]
}

#[inline]
fn f32x4_to_f16x4_fallback(v: &[f32]) -> [u16; 4] {
    debug_assert!(v.len() >= 4);

    [
        f32_to_f16_fallback(v[0]),
        f32_to_f16_fallback(v[1]),
        f32_to_f16_fallback(v[2]),
        f32_to_f16_fallback(v[3]),
    ]
}

#[inline]
fn f16x4_to_f64x4_fallback(v: &[u16]) -> [f64; 4] {
    debug_assert!(v.len() >= 4);

    [
        f16_to_f64_fallback(v[0]),
        f16_to_f64_fallback(v[1]),
        f16_to_f64_fallback(v[2]),
        f16_to_f64_fallback(v[3]),
    ]
}

#[inline]
fn f64x4_to_f16x4_fallback(v: &[f64]) -> [u16; 4] {
    debug_assert!(v.len() >= 4);

    [
        f64_to_f16_fallback(v[0]),
        f64_to_f16_fallback(v[1]),
        f64_to_f16_fallback(v[2]),
        f64_to_f16_fallback(v[3]),
    ]
}

/////////////// x86/x86_64 f16c ////////////////
#[cfg(all(
    feature = "use-intrinsics",
    any(target_arch = "x86", target_arch = "x86_64")
))]
mod x86 {
    use core::{mem::MaybeUninit, ptr};

    #[cfg(target_arch = "x86")]
    use core::arch::x86::{__m128, __m128i, _mm_cvtph_ps, _mm_cvtps_ph, _MM_FROUND_TO_NEAREST_INT};
    #[cfg(target_arch = "x86_64")]
    use core::arch::x86_64::{
        __m128, __m128i, _mm_cvtph_ps, _mm_cvtps_ph, _MM_FROUND_TO_NEAREST_INT,
    };

    #[target_feature(enable = "f16c")]
    #[inline]
    pub(superunsafe fn f16_to_f32_x86_f16c(i: u16) -> f32 {
        let mut vec = MaybeUninit::<__m128i>::zeroed();
        vec.as_mut_ptr().cast::<u16>().write(i);
        let retval = _mm_cvtph_ps(vec.assume_init());
        *(&retval as *const __m128).cast()
    }

    #[target_feature(enable = "f16c")]
    #[inline]
    pub(superunsafe fn f32_to_f16_x86_f16c(f: f32) -> u16 {
        let mut vec = MaybeUninit::<__m128>::zeroed();
        vec.as_mut_ptr().cast::<f32>().write(f);
        let retval = _mm_cvtps_ph(vec.assume_init(), _MM_FROUND_TO_NEAREST_INT);
        *(&retval as *const __m128i).cast()
    }

    #[target_feature(enable = "f16c")]
    #[inline]
    pub(superunsafe fn f16x4_to_f32x4_x86_f16c(v: &[u16]) -> [f32; 4] {
        debug_assert!(v.len() >= 4);

        let mut vec = MaybeUninit::<__m128i>::zeroed();
        ptr::copy_nonoverlapping(v.as_ptr(), vec.as_mut_ptr().cast(), 4);
        let retval = _mm_cvtph_ps(vec.assume_init());
        *(&retval as *const __m128).cast()
    }

    #[target_feature(enable = "f16c")]
    #[inline]
    pub(superunsafe fn f32x4_to_f16x4_x86_f16c(v: &[f32]) -> [u16; 4] {
        debug_assert!(v.len() >= 4);

        let mut vec = MaybeUninit::<__m128>::uninit();
        ptr::copy_nonoverlapping(v.as_ptr(), vec.as_mut_ptr().cast(), 4);
        let retval = _mm_cvtps_ph(vec.assume_init(), _MM_FROUND_TO_NEAREST_INT);
        *(&retval as *const __m128i).cast()
    }

    #[target_feature(enable = "f16c")]
    #[inline]
    pub(superunsafe fn f16x4_to_f64x4_x86_f16c(v: &[u16]) -> [f64; 4] {
        debug_assert!(v.len() >= 4);

        let mut vec = MaybeUninit::<__m128i>::zeroed();
        ptr::copy_nonoverlapping(v.as_ptr(), vec.as_mut_ptr().cast(), 4);
        let retval = _mm_cvtph_ps(vec.assume_init());
        let array = *(&retval as *const __m128).cast::<[f32; 4]>();
        // Let compiler vectorize this regular cast for now.
        // TODO: investigate auto-detecting sse2/avx convert features
        [
            array[0as f64,
            array[1as f64,
            array[2as f64,
            array[3as f64,
        ]
    }

    #[target_feature(enable = "f16c")]
    #[inline]
    pub(superunsafe fn f64x4_to_f16x4_x86_f16c(v: &[f64]) -> [u16; 4] {
        debug_assert!(v.len() >= 4);

        // Let compiler vectorize this regular cast for now.
        // TODO: investigate auto-detecting sse2/avx convert features
        let v = [v[0as f32, v[1as f32, v[2as f32, v[3as f32];

        let mut vec = MaybeUninit::<__m128>::uninit();
        ptr::copy_nonoverlapping(v.as_ptr(), vec.as_mut_ptr().cast(), 4);
        let retval = _mm_cvtps_ph(vec.assume_init(), _MM_FROUND_TO_NEAREST_INT);
        *(&retval as *const __m128i).cast()
    }
}

Messung V0.5 in Prozent
C=85 H=89 G=86

¤ Dauer der Verarbeitung: 0.12 Sekunden  (vorverarbeitet am  2026-06-23) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.