Implement feature isolate_most_least_significant_one for integer types

Implement accepted ACP for functions that isolate the most significant
set bit and least significant set bit on unsigned, signed, and NonZero
integers.

Add function `isolate_most_significant_one`
Add function `isolate_least_significant_one`
Add tests
This commit is contained in:
okaneco 2025-02-12 02:14:33 -05:00
parent ffa9afef18
commit 97bc99a18f
7 changed files with 325 additions and 0 deletions

View File

@ -183,6 +183,52 @@ macro_rules! int_impl {
(self as $UnsignedT).trailing_ones()
}
/// Returns `self` with only the most significant bit set, or `0` if
/// the input is `0`.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(isolate_most_least_significant_one)]
///
#[doc = concat!("let n: ", stringify!($SelfT), " = 0b_01100100;")]
///
/// assert_eq!(n.isolate_most_significant_one(), 0b_01000000);
#[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".isolate_most_significant_one(), 0);")]
/// ```
#[unstable(feature = "isolate_most_least_significant_one", issue = "136909")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline(always)]
pub const fn isolate_most_significant_one(self) -> Self {
self & (((1 as $SelfT) << (<$SelfT>::BITS - 1)).wrapping_shr(self.leading_zeros()))
}
/// Returns `self` with only the least significant bit set, or `0` if
/// the input is `0`.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(isolate_most_least_significant_one)]
///
#[doc = concat!("let n: ", stringify!($SelfT), " = 0b_01100100;")]
///
/// assert_eq!(n.isolate_least_significant_one(), 0b_00000100);
#[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".isolate_least_significant_one(), 0);")]
/// ```
#[unstable(feature = "isolate_most_least_significant_one", issue = "136909")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline(always)]
pub const fn isolate_least_significant_one(self) -> Self {
self & self.wrapping_neg()
}
/// Returns the bit pattern of `self` reinterpreted as an unsigned integer of the same size.
///
/// This produces the same result as an `as` cast, but ensures that the bit-width remains

View File

@ -605,6 +605,70 @@ macro_rules! nonzero_integer {
}
}
/// Returns `self` with only the most significant bit set.
///
/// # Example
///
/// Basic usage:
///
/// ```
/// #![feature(isolate_most_least_significant_one)]
///
/// # use core::num::NonZero;
/// # fn main() { test().unwrap(); }
/// # fn test() -> Option<()> {
#[doc = concat!("let a = NonZero::<", stringify!($Int), ">::new(0b_01100100)?;")]
#[doc = concat!("let b = NonZero::<", stringify!($Int), ">::new(0b_01000000)?;")]
///
/// assert_eq!(a.isolate_most_significant_one(), b);
/// # Some(())
/// # }
/// ```
#[unstable(feature = "isolate_most_least_significant_one", issue = "136909")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline(always)]
pub const fn isolate_most_significant_one(self) -> Self {
let n = self.get() & (((1 as $Int) << (<$Int>::BITS - 1)).wrapping_shr(self.leading_zeros()));
// SAFETY:
// `self` is non-zero, so masking to preserve only the most
// significant set bit will result in a non-zero `n`.
unsafe { NonZero::new_unchecked(n) }
}
/// Returns `self` with only the least significant bit set.
///
/// # Example
///
/// Basic usage:
///
/// ```
/// #![feature(isolate_most_least_significant_one)]
///
/// # use core::num::NonZero;
/// # fn main() { test().unwrap(); }
/// # fn test() -> Option<()> {
#[doc = concat!("let a = NonZero::<", stringify!($Int), ">::new(0b_01100100)?;")]
#[doc = concat!("let b = NonZero::<", stringify!($Int), ">::new(0b_00000100)?;")]
///
/// assert_eq!(a.isolate_least_significant_one(), b);
/// # Some(())
/// # }
/// ```
#[unstable(feature = "isolate_most_least_significant_one", issue = "136909")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline(always)]
pub const fn isolate_least_significant_one(self) -> Self {
let n = self.get();
let n = n & n.wrapping_neg();
// SAFETY: `self` is non-zero, so `self` with only its least
// significant set bit will remain non-zero.
unsafe { NonZero::new_unchecked(n) }
}
/// Returns the number of ones in the binary representation of `self`.
///
/// # Examples

View File

@ -213,6 +213,52 @@ macro_rules! uint_impl {
(!self).trailing_zeros()
}
/// Returns `self` with only the most significant bit set, or `0` if
/// the input is `0`.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(isolate_most_least_significant_one)]
///
#[doc = concat!("let n: ", stringify!($SelfT), " = 0b_01100100;")]
///
/// assert_eq!(n.isolate_most_significant_one(), 0b_01000000);
#[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".isolate_most_significant_one(), 0);")]
/// ```
#[unstable(feature = "isolate_most_least_significant_one", issue = "136909")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline(always)]
pub const fn isolate_most_significant_one(self) -> Self {
self & (((1 as $SelfT) << (<$SelfT>::BITS - 1)).wrapping_shr(self.leading_zeros()))
}
/// Returns `self` with only the least significant bit set, or `0` if
/// the input is `0`.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(isolate_most_least_significant_one)]
///
#[doc = concat!("let n: ", stringify!($SelfT), " = 0b_01100100;")]
///
/// assert_eq!(n.isolate_least_significant_one(), 0b_00000100);
#[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".isolate_least_significant_one(), 0);")]
/// ```
#[unstable(feature = "isolate_most_least_significant_one", issue = "136909")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline(always)]
pub const fn isolate_least_significant_one(self) -> Self {
self & self.wrapping_neg()
}
/// Returns the bit pattern of `self` reinterpreted as a signed integer of the same size.
///
/// This produces the same result as an `as` cast, but ensures that the bit-width remains

View File

@ -43,6 +43,7 @@
#![feature(ip)]
#![feature(ip_from)]
#![feature(is_ascii_octdigit)]
#![feature(isolate_most_least_significant_one)]
#![feature(iter_advance_by)]
#![feature(iter_array_chunks)]
#![feature(iter_chain)]

View File

@ -321,6 +321,106 @@ fn nonzero_trailing_zeros() {
assert_eq!(TRAILING_ZEROS, 2);
}
#[test]
fn test_nonzero_isolate_most_significant_one() {
// Signed most significant one
macro_rules! nonzero_int_impl {
($($T:ty),+) => {
$(
{
const BITS: $T = -1;
const MOST_SIG_ONE: $T = 1 << (<$T>::BITS - 1);
// Right shift the most significant one through each
// bit position, starting with all bits set
let mut i = 0;
while i < <$T>::BITS {
assert_eq!(
NonZero::<$T>::new(BITS >> i).unwrap().isolate_most_significant_one(),
NonZero::<$T>::new(MOST_SIG_ONE >> i).unwrap().isolate_most_significant_one()
);
i += 1;
}
}
)+
};
}
// Unsigned most significant one
macro_rules! nonzero_uint_impl {
($($T:ty),+) => {
$(
{
const BITS: $T = <$T>::MAX;
const MOST_SIG_ONE: $T = 1 << (<$T>::BITS - 1);
let mut i = 0;
while i < <$T>::BITS {
assert_eq!(
NonZero::<$T>::new(BITS >> i).unwrap().isolate_most_significant_one(),
NonZero::<$T>::new(MOST_SIG_ONE >> i).unwrap().isolate_most_significant_one(),
);
i += 1;
}
}
)+
};
}
nonzero_int_impl!(i8, i16, i32, i64, i128, isize);
nonzero_uint_impl!(u8, u16, u32, u64, u128, usize);
}
#[test]
fn test_nonzero_isolate_least_significant_one() {
// Signed least significant one
macro_rules! nonzero_int_impl {
($($T:ty),+) => {
$(
{
const BITS: $T = -1;
const LEAST_SIG_ONE: $T = 1;
// Left shift the least significant one through each
// bit position, starting with all bits set
let mut i = 0;
while i < <$T>::BITS {
assert_eq!(
NonZero::<$T>::new(BITS << i).unwrap().isolate_least_significant_one(),
NonZero::<$T>::new(LEAST_SIG_ONE << i).unwrap().isolate_least_significant_one()
);
i += 1;
}
}
)+
};
}
// Unsigned least significant one
macro_rules! nonzero_uint_impl {
($($T:ty),+) => {
$(
{
const BITS: $T = <$T>::MAX;
const LEAST_SIG_ONE: $T = 1;
let mut i = 0;
while i < <$T>::BITS {
assert_eq!(
NonZero::<$T>::new(BITS << i).unwrap().isolate_least_significant_one(),
NonZero::<$T>::new(LEAST_SIG_ONE << i).unwrap().isolate_least_significant_one(),
);
i += 1;
}
}
)+
};
}
nonzero_int_impl!(i8, i16, i32, i64, i128, isize);
nonzero_uint_impl!(u8, u16, u32, u64, u128, usize);
}
#[test]
fn test_nonzero_uint_div() {
let nz = NonZero::new(1).unwrap();

View File

@ -192,6 +192,40 @@ macro_rules! int_module {
}
}
#[test]
fn test_isolate_most_significant_one() {
const BITS: $T = -1;
const MOST_SIG_ONE: $T = 1 << (<$T>::BITS - 1);
// Right shift the most significant one through each
// bit position, starting with all bits set
let mut i = 0;
while i < <$T>::BITS {
assert_eq!(
(BITS >> i).isolate_most_significant_one(),
(MOST_SIG_ONE >> i).isolate_most_significant_one()
);
i += 1;
}
}
#[test]
fn test_isolate_least_significant_one() {
const BITS: $T = -1;
const LEAST_SIG_ONE: $T = 1;
// Left shift the least significant one through each
// bit position, starting with all bits set
let mut i = 0;
while i < <$T>::BITS {
assert_eq!(
(BITS << i).isolate_least_significant_one(),
(LEAST_SIG_ONE << i).isolate_least_significant_one()
);
i += 1;
}
}
#[test]
fn test_from_str() {
fn from_str<T: std::str::FromStr>(t: &str) -> Option<T> {

View File

@ -141,6 +141,40 @@ macro_rules! uint_module {
}
}
#[test]
fn test_isolate_most_significant_one() {
const BITS: $T = <$T>::MAX;
const MOST_SIG_ONE: $T = 1 << (<$T>::BITS - 1);
// Right shift the most significant one through each
// bit position, starting with all bits set
let mut i = 0;
while i < <$T>::BITS {
assert_eq!(
(BITS >> i).isolate_most_significant_one(),
(MOST_SIG_ONE >> i).isolate_most_significant_one(),
);
i += 1;
}
}
#[test]
fn test_isolate_least_significant_one() {
const BITS: $T = <$T>::MAX;
const LEAST_SIG_ONE: $T = 1;
// Left shift the least significant one through each
// bit position, starting with all bits set
let mut i = 0;
while i < <$T>::BITS {
assert_eq!(
(BITS << i).isolate_least_significant_one(),
(LEAST_SIG_ONE << i).isolate_least_significant_one(),
);
i += 1;
}
}
fn from_str<T: core::str::FromStr>(t: &str) -> Option<T> {
core::str::FromStr::from_str(t).ok()
}