The following function is an attempt to generate powers of two below the input number max
.
So powers_of_two_below(15u8, u8::BITS)
generates 1, 2, 4, 8. It does calculate 16, but does not include it. This function avoids calculating powers that cause overflow by taking bits
as input (since there doesn't appear to be a standard trait that exposes T::BITS
). It can be re-used for several number types.
But it doesn't seem very concise. Can you recommend ways to improve it?
use num_traits::{One, Zero};
use std::ops::Shl;
pub fn powers_of_two_below<T>(max: T, bits: u32) -> impl Iterator<Item = T>
where
T: Zero + One + PartialOrd + Shl<Output = T> + Copy,
{
let mut exp = T::zero();
(0..bits)
.map(move |_| {
let power = T::one() << exp;
exp = exp + T::one();
power
})
.take_while(move |&x| x < max)
}
I tried to make a more concise version:
pub fn powers_of_two_below<T>(max: T, bits: u32) -> impl Iterator<Item = T>
where
T: One + PartialOrd + Shl<Output = T> + Copy + From<u32>,
{
(0..bits)
.map(|i| T::one() << i.into())
.take_while(move |&x| x < max)
}
...but it is more impractical: A lot of number types don't have From<u32>
(including usize
and uN
where N
< 32).
1 Answer 1
By specifying the right hand side of Shl
, you can remove the From<u32>
entirely:
fn powers_of_two_below<T>(max: T, bits: u32) -> impl Iterator<Item = T>
where
T: One + PartialOrd + Shl<u32, Output = T> + Copy,
{
(0..bits)
.map(|i: u32| T::one() << i)
.take_while(move |&x| x < max)
}
If you wanted to remove the bits
parameter, you'd probably have to implement your own trait specifically for it, but I'm not sure how you'd define bits
for floating point numbers.
Here's an example where I defined a Bits
trait, using a macro to implement it for all integer types. I also used it to replace num_traits::One
because why not.