Stupid or genius? Getting at the BITS

All the standard integer type have a pub const BITS.
Unfortunately the only way to get at them is by spelling their name, i.e. u32::BITS.
Would it be stupid to therefore resort to something like:

trait BitWide {
	const BITS: u32;
	fn width(&self) -> u32 {
		Self::BITS
	}
}
macro_rules! bitwide_impl {
	($( $T:tt ),*) => {
		$(impl BitWide for $T {
			const BITS: u32 = $T::BITS;
		})*
	};
}

bitwide_impl!(u8, u16, u32, u64, u128, usize);
bitwide_impl!(i8, i16, i32, i64, i128, isize);

...
let x = 1u128;
let _ = x.width();

Since that's a lot of machinery to avoid writing a few types.
Also can anyone explain why rust uses u32 for this and things like leading_zeros()?

Edit: I was trying to implement next_power_of_two()

Can you say why you want to know this from an instance?

My preferred solution is to not care, and just not use the methods where it matters -- like use .ilog2() instead of .leading_zeroes().

I'm trying to round up to next power of two (for fun).
Which is 1 << (u32::BITS - x.leading_zeros() - 1); for a u32 x.
But I'd rather not write down the type.

I know that's perhaps not a lot of fun, but there's next_power_of_two() on all integer types.

8 Likes

That is indeed less fun. Still I can't believe I missed that one.

4 Likes

This is why I suggest just not using leading_zeros. You can do 1 << x.ilog2() instead and not need to know the width. (Well, not exactly that, but that with some fixups, if you want to implement next_power_of_two yourself.)

ah

1 << ((x - 1).ilog2() +1);

That brings it in line with next_power_of_two() returning the next greater or equal power of two.
That is more fun indeed.
Gonna check godbolt.

Edit: Seems solid, though since it can panic I have to do this to get it inlined:

1 << ((x-1).checked_ilog2().unwrap() +1)

And this should not be called with 0 or 1 ooops.

TL;DR: just use the standard version. Or stick to
1 << (x.ilog2()+1) if you need different semantics.

The libcore version does a bunch of stuff to handle the annoying fact that shifting by bitwidth doesn't do what you want, for example.

It's basically https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2: fill the bits below the highest one (which can't overflow, and gets you to one less than a power of two) then adds one.

1 Like

Is libcore a different library? The one I checked used
intrinsics::ctlz_nonzero(p) in the absolutely hilariously named one_less_than_next_power_of_two function.

Can't overflow if you don't do the addition, Hah!

Addressing the original question, I expected the num-traits crate to have this, but:

All PrimInt types are expected to be fixed-width binary integers. The width can be queried via T::zero().count_zeros(). The trait currently lacks a way to query the width at compile-time.

I wonder if there's a technical reason?

A similar issue came up with the funty trait and trying to get the byte length for a number, which would give the number of bits also. There was an explanation given:

Probably not the same, but perhaps related?

There's a semver reason it can't be added to 0.2.* (it's not a sealed trait).

1 Like

Of course it's very easy to implement with num_traits and size_of.

use core::mem::size_of;
use num_traits::PrimInt;

const fn bit_size<I: PrimInt>() -> u32 {
    8 * (size_of::<I>() as u32)
}

const fn bit_size_of_val<I: PrimInt>(_: I) -> u32 {
    bit_size::<I>()
}
1 Like

libcore is the old name for just the core library you're looking at.

The !0 >> ctlz_nonzero(x) approach it's using is just a faster way of doing the shifts from the bithacks link. So it's the same conceptual approach, albeit different instructions.

I wish I could think of a better name than one_less_than_next_power_of_two so we could stabilize it :upside_down_face:

I was thinking associated const, but that's not an actual solution either.

#[derive(Copy, Clone)]
enum MyInterestingSupplementalData {
    Var1,
    Var2,
}

#[derive(Copy, Clone)]
pub struct MyInt {
    data: MyInterestingSupplementalData,
    int: u64,
}

// Too lazy to implement a bazillion supertraits, but anyway
fn main() {
    assert_ne!(
        8 * (size_of::<MyInt>() as u32),
        64
    );
}

Ok, I see. I was just thinking of the OP where they wanted to implement it for the primitive ints.

nearly_next_power_of_two(), just_one_wafer_thin_bit_sir(), juat_look_at_all_these_ones_in_an_easy_to_carry_primitive(),
maybe_this_is_a_prime_who_knows()

But more seriously, they're called Mersenne numbers in maths, you're not likely to get a snappier name than that. Main problem is the semantics: it's not the next Mersenne number if it already is one, right?

6 Likes

mersenne_ceil?

4 Likes

That's also true for next_power_of_two so I wouldn't mind a next_mersenne_number function.

However given the mathematical importance of Mersenne Primes and their enormous sizes I would also want a BigInt with next_mersenne_prime. So you might want to fight over who gets a shorter name.

Perhaps a function to get the p part of (2^p -1) and the exponent of the next power of two could be useful too idk.

huh, i wouldn't expect something called "next" to return the current power of two, but its more likely to be what you want, and consistency trumps everything.