Skip to content

Commit 93ce1c1

Browse files
committed
Add floating-point classification functions
1 parent 4e6d440 commit 93ce1c1

File tree

5 files changed

+163
-70
lines changed

5 files changed

+163
-70
lines changed

Diff for: crates/core_simd/src/comparisons.rs

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
use crate::LanesAtMost64;
2+
3+
macro_rules! implement_mask_ops {
4+
{ $($vector:ident => $mask:ident ($inner_mask_ty:ident, $inner_ty:ident),)* } => {
5+
$(
6+
impl<const LANES: usize> crate::$vector<LANES>
7+
where
8+
crate::$vector<LANES>: LanesAtMost64,
9+
crate::$inner_ty<LANES>: LanesAtMost64,
10+
{
11+
/// Test if each lane is equal to the corresponding lane in `other`.
12+
#[inline]
13+
pub fn lanes_eq(self, other: Self) -> crate::$mask<LANES> {
14+
unsafe {
15+
crate::$inner_mask_ty::from_int_unchecked(crate::intrinsics::simd_eq(self, other))
16+
.into()
17+
}
18+
}
19+
20+
/// Test if each lane is not equal to the corresponding lane in `other`.
21+
#[inline]
22+
pub fn lanes_ne(self, other: Self) -> crate::$mask<LANES> {
23+
unsafe {
24+
crate::$inner_mask_ty::from_int_unchecked(crate::intrinsics::simd_ne(self, other))
25+
.into()
26+
}
27+
}
28+
29+
/// Test if each lane is less than the corresponding lane in `other`.
30+
#[inline]
31+
pub fn lanes_lt(self, other: Self) -> crate::$mask<LANES> {
32+
unsafe {
33+
crate::$inner_mask_ty::from_int_unchecked(crate::intrinsics::simd_lt(self, other))
34+
.into()
35+
}
36+
}
37+
38+
/// Test if each lane is greater than the corresponding lane in `other`.
39+
#[inline]
40+
pub fn lanes_gt(self, other: Self) -> crate::$mask<LANES> {
41+
unsafe {
42+
crate::$inner_mask_ty::from_int_unchecked(crate::intrinsics::simd_gt(self, other))
43+
.into()
44+
}
45+
}
46+
47+
/// Test if each lane is less than or equal to the corresponding lane in `other`.
48+
#[inline]
49+
pub fn lanes_le(self, other: Self) -> crate::$mask<LANES> {
50+
unsafe {
51+
crate::$inner_mask_ty::from_int_unchecked(crate::intrinsics::simd_le(self, other))
52+
.into()
53+
}
54+
}
55+
56+
/// Test if each lane is greater than or equal to the corresponding lane in `other`.
57+
#[inline]
58+
pub fn lanes_ge(self, other: Self) -> crate::$mask<LANES> {
59+
unsafe {
60+
crate::$inner_mask_ty::from_int_unchecked(crate::intrinsics::simd_ge(self, other))
61+
.into()
62+
}
63+
}
64+
}
65+
)*
66+
}
67+
}
68+
69+
implement_mask_ops! {
70+
SimdI8 => Mask8 (SimdMask8, SimdI8),
71+
SimdI16 => Mask16 (SimdMask16, SimdI16),
72+
SimdI32 => Mask32 (SimdMask32, SimdI32),
73+
SimdI64 => Mask64 (SimdMask64, SimdI64),
74+
SimdI128 => Mask128 (SimdMask128, SimdI128),
75+
SimdIsize => MaskSize (SimdMaskSize, SimdIsize),
76+
77+
SimdU8 => Mask8 (SimdMask8, SimdI8),
78+
SimdU16 => Mask16 (SimdMask16, SimdI16),
79+
SimdU32 => Mask32 (SimdMask32, SimdI32),
80+
SimdU64 => Mask64 (SimdMask64, SimdI64),
81+
SimdU128 => Mask128 (SimdMask128, SimdI128),
82+
SimdUsize => MaskSize (SimdMaskSize, SimdIsize),
83+
84+
SimdF32 => Mask32 (SimdMask32, SimdI32),
85+
SimdF64 => Mask64 (SimdMask64, SimdI64),
86+
}

Diff for: crates/core_simd/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ mod fmt;
1616
mod intrinsics;
1717
mod ops;
1818
mod round;
19+
mod comparisons;
1920

2021
mod math;
2122

Diff for: crates/core_simd/src/masks/full_masks.rs

+19
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,25 @@ macro_rules! define_mask {
7575
0
7676
}
7777
}
78+
79+
/// Creates a mask from an integer vector.
80+
///
81+
/// # Safety
82+
/// All lanes must be either 0 or -1.
83+
#[inline]
84+
pub unsafe fn from_int_unchecked(value: $type) -> Self {
85+
Self(value)
86+
}
87+
88+
/// Creates a mask from an integer vector.
89+
///
90+
/// # Panics
91+
/// Panics if any lane is not 0 or -1.
92+
#[inline]
93+
pub fn from_int(value: $type) -> Self {
94+
use core::convert::TryInto;
95+
value.try_into().unwrap()
96+
}
7897
}
7998

8099
impl<const $lanes: usize> core::convert::From<bool> for $name<$lanes>

Diff for: crates/core_simd/src/masks/mod.rs

-67
Original file line numberDiff line numberDiff line change
@@ -360,73 +360,6 @@ define_opaque_mask! {
360360
@bits crate::SimdIsize<LANES>
361361
}
362362

363-
macro_rules! implement_mask_ops {
364-
{ $($vector:ident => $mask:ident ($inner_ty:ident),)* } => {
365-
$(
366-
impl<const LANES: usize> crate::$vector<LANES>
367-
where
368-
crate::$vector<LANES>: LanesAtMost64,
369-
crate::$inner_ty<LANES>: LanesAtMost64,
370-
{
371-
/// Test if each lane is equal to the corresponding lane in `other`.
372-
#[inline]
373-
pub fn lanes_eq(&self, other: &Self) -> $mask<LANES> {
374-
unsafe { $mask(crate::intrinsics::simd_eq(self, other)) }
375-
}
376-
377-
/// Test if each lane is not equal to the corresponding lane in `other`.
378-
#[inline]
379-
pub fn lanes_ne(&self, other: &Self) -> $mask<LANES> {
380-
unsafe { $mask(crate::intrinsics::simd_ne(self, other)) }
381-
}
382-
383-
/// Test if each lane is less than the corresponding lane in `other`.
384-
#[inline]
385-
pub fn lanes_lt(&self, other: &Self) -> $mask<LANES> {
386-
unsafe { $mask(crate::intrinsics::simd_lt(self, other)) }
387-
}
388-
389-
/// Test if each lane is greater than the corresponding lane in `other`.
390-
#[inline]
391-
pub fn lanes_gt(&self, other: &Self) -> $mask<LANES> {
392-
unsafe { $mask(crate::intrinsics::simd_gt(self, other)) }
393-
}
394-
395-
/// Test if each lane is less than or equal to the corresponding lane in `other`.
396-
#[inline]
397-
pub fn lanes_le(&self, other: &Self) -> $mask<LANES> {
398-
unsafe { $mask(crate::intrinsics::simd_le(self, other)) }
399-
}
400-
401-
/// Test if each lane is greater than or equal to the corresponding lane in `other`.
402-
#[inline]
403-
pub fn lanes_ge(&self, other: &Self) -> $mask<LANES> {
404-
unsafe { $mask(crate::intrinsics::simd_ge(self, other)) }
405-
}
406-
}
407-
)*
408-
}
409-
}
410-
411-
implement_mask_ops! {
412-
SimdI8 => Mask8 (SimdI8),
413-
SimdI16 => Mask16 (SimdI16),
414-
SimdI32 => Mask32 (SimdI32),
415-
SimdI64 => Mask64 (SimdI64),
416-
SimdI128 => Mask128 (SimdI128),
417-
SimdIsize => MaskSize (SimdIsize),
418-
419-
SimdU8 => Mask8 (SimdI8),
420-
SimdU16 => Mask16 (SimdI16),
421-
SimdU32 => Mask32 (SimdI32),
422-
SimdU64 => Mask64 (SimdI64),
423-
SimdU128 => Mask128 (SimdI128),
424-
SimdUsize => MaskSize (SimdIsize),
425-
426-
SimdF32 => Mask32 (SimdI32),
427-
SimdF64 => Mask64 (SimdI64),
428-
}
429-
430363
/// Vector of eight 8-bit masks
431364
pub type mask8x8 = Mask8<8>;
432365

Diff for: crates/core_simd/src/vector/float.rs

+57-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
/// `$lanes` of float `$type`, which uses `$bits_ty` as its binary
55
/// representation. Called from `define_float_vector!`.
66
macro_rules! impl_float_vector {
7-
{ $name:ident, $type:ty, $bits_ty:ident } => {
7+
{ $name:ident, $type:ty, $bits_ty:ident, $mask_ty:ident, $mask_impl_ty:ident } => {
88
impl_vector! { $name, $type }
99

1010
impl<const LANES: usize> $name<LANES>
@@ -36,6 +36,60 @@ macro_rules! impl_float_vector {
3636
Self::from_bits(self.to_bits() & no_sign)
3737
}
3838
}
39+
40+
impl<const LANES: usize> $name<LANES>
41+
where
42+
Self: crate::LanesAtMost64,
43+
crate::$bits_ty<LANES>: crate::LanesAtMost64,
44+
crate::$mask_impl_ty<LANES>: crate::LanesAtMost64,
45+
{
46+
/// Returns true for each lane if it has a positive sign, including
47+
/// `+0.0`, `NaN`s with positive sign bit and positive infinity.
48+
#[inline]
49+
pub fn is_sign_positive(self) -> crate::$mask_ty<LANES> {
50+
let sign_bits = self.to_bits() & crate::$bits_ty::splat((!0 >> 1) + 1);
51+
sign_bits.lanes_gt(crate::$bits_ty::splat(0))
52+
}
53+
54+
/// Returns true for each lane if it has a negative sign, including
55+
/// `-0.0`, `NaN`s with negative sign bit and negative infinity.
56+
#[inline]
57+
pub fn is_sign_negative(self) -> crate::$mask_ty<LANES> {
58+
!self.is_sign_positive()
59+
}
60+
61+
/// Returns true for each lane if its value is `NaN`.
62+
#[inline]
63+
pub fn is_nan(self) -> crate::$mask_ty<LANES> {
64+
self.lanes_eq(self)
65+
}
66+
67+
/// Returns true for each lane if its value is positive infinity or negative infinity.
68+
#[inline]
69+
pub fn is_infinite(self) -> crate::$mask_ty<LANES> {
70+
self.abs().lanes_eq(Self::splat(<$type>::INFINITY))
71+
}
72+
73+
/// Returns true for each lane if its value is neither infinite nor `NaN`.
74+
#[inline]
75+
pub fn is_finite(self) -> crate::$mask_ty<LANES> {
76+
self.abs().lanes_lt(Self::splat(<$type>::INFINITY))
77+
}
78+
79+
/// Returns true for each lane if its value is subnormal.
80+
#[inline]
81+
pub fn is_subnormal(self) -> crate::$mask_ty<LANES> {
82+
let mantissa_mask = crate::$bits_ty::splat((1 << (<$type>::MANTISSA_DIGITS - 1)) - 1);
83+
self.abs().lanes_ne(Self::splat(0.0)) & (self.to_bits() & mantissa_mask).lanes_eq(crate::$bits_ty::splat(0))
84+
}
85+
86+
/// Returns true for each lane if its value is neither neither zero, infinite,
87+
/// subnormal, or `NaN`.
88+
#[inline]
89+
pub fn is_normal(self) -> crate::$mask_ty<LANES> {
90+
!(self.abs().lanes_eq(Self::splat(0.0)) | self.is_nan() | self.is_subnormal())
91+
}
92+
}
3993
};
4094
}
4195

@@ -46,7 +100,7 @@ pub struct SimdF32<const LANES: usize>([f32; LANES])
46100
where
47101
Self: crate::LanesAtMost64;
48102

49-
impl_float_vector! { SimdF32, f32, SimdU32 }
103+
impl_float_vector! { SimdF32, f32, SimdU32, Mask32, SimdI32 }
50104

51105
from_transmute_x86! { unsafe f32x4 => __m128 }
52106
from_transmute_x86! { unsafe f32x8 => __m256 }
@@ -58,7 +112,7 @@ pub struct SimdF64<const LANES: usize>([f64; LANES])
58112
where
59113
Self: crate::LanesAtMost64;
60114

61-
impl_float_vector! { SimdF64, f64, SimdU64 }
115+
impl_float_vector! { SimdF64, f64, SimdU64, Mask64, SimdI64 }
62116

63117
from_transmute_x86! { unsafe f64x2 => __m128d }
64118
from_transmute_x86! { unsafe f64x4 => __m256d }

0 commit comments

Comments
 (0)