Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions doc/modules/ROOT/pages/api_reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,9 @@ https://www.boost.org/LICENSE_1_0.txt

| xref:integer_utilities.adoc[`is_power_10`]
| Tests whether a safe unsigned integer is an exact power of 10

| xref:integer_utilities.adoc[`is_power_2`]
| Tests whether a safe unsigned integer is an exact power of 2
|===

=== Arithmetic
Expand Down
65 changes: 59 additions & 6 deletions doc/modules/ROOT/pages/integer_utilities.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ It processes the exponent in O(log(max_digits)) steps, where `max_digits` is the

==== Parameters

* `n` -- The value to remove trailing zeros from. Must be non-zero.
* `n` -- The value to remove trailing zeros from. If `n` is zero, returns `{0, 0}`.

==== Return Value

Expand Down Expand Up @@ -174,7 +174,7 @@ template <non_bounded_unsigned_library_type T>
consteval auto remove_trailing_zeros(const verified_type_basis<T> val);
----

Compile-time only overload for verified types.
Compile-time-only overload for verified types.
Delegates to the detail implementation after extracting the underlying value.

Since `remove_trailing_zeros` is `consteval` for verified types, the result is guaranteed to be a compile-time constant.
Expand All @@ -193,8 +193,6 @@ constexpr auto r = remove_trailing_zeros(verified_u32{u32{5000}});

Tests whether an unsigned integer value is an exact power of 10 (i.e., one of 1, 10, 100, 1000, ...).

Implemented using `remove_trailing_zeros` and checking that the trimmed result equals 1.

=== Runtime Overload

[source,c++]
Expand All @@ -206,7 +204,7 @@ constexpr auto is_power_10(const T n) -> bool;

==== Parameters

* `n` -- The value to test. Must be non-zero.
* `n` -- The value to test.

==== Return Value

Expand All @@ -232,7 +230,7 @@ template <non_bounded_unsigned_library_type T>
consteval auto is_power_10(const verified_type_basis<T> n) -> bool;
----

Compile-time only overload for verified types.
Compile-time-only overload for verified types.

Since `is_power_10` is `consteval` for verified types, the result is guaranteed to be a compile-time constant and can be used directly in `static_assert`.

Expand All @@ -245,3 +243,58 @@ using namespace boost::safe_numbers;
static_assert(is_power_10(verified_u32{u32{100}}));
static_assert(!is_power_10(verified_u32{u32{200}}));
----

== is_power_2

Tests whether an unsigned integer value is an exact power of 2 (i.e., has exactly one bit set).

=== Runtime Overload

[source,c++]
----
template <non_bounded_unsigned_library_type T>
requires (!is_verified_type_v<T>)
constexpr auto is_power_2(const T n) noexcept -> bool;
----

==== Parameters

* `n` -- The value to test.

==== Return Value

`true` if `n` is a power of 2, `false` otherwise.

==== Example

[source,c++]
----
using namespace boost::safe_numbers;

is_power_2(u32{1024}); // true
is_power_2(u32{1}); // true
is_power_2(u32{1000}); // false
is_power_2(u64{9223372036854775808ULL}); // true (2^63)
----

=== Verified Overload

[source,c++]
----
template <non_bounded_unsigned_library_type T>
consteval auto is_power_2(const verified_type_basis<T> n) noexcept -> bool;
----

Compile-time-only overload for verified types.

Since `is_power_2` is `consteval` for verified types, the result is guaranteed to be a compile-time constant and can be used directly in `static_assert`.

==== Example

[source,c++]
----
using namespace boost::safe_numbers;

static_assert(is_power_2(verified_u32{u32{1048576}})); // 2^20
static_assert(!is_power_2(verified_u32{u32{1000000}}));
----
26 changes: 26 additions & 0 deletions include/boost/safe_numbers/integer_utilities.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <boost/safe_numbers/detail/config.hpp>
#include <boost/safe_numbers/detail/type_traits.hpp>
#include <boost/safe_numbers/detail/rtz.hpp>
#include <boost/safe_numbers/bit.hpp>

namespace boost::safe_numbers {

Expand Down Expand Up @@ -51,13 +52,25 @@ template <detail::non_bounded_unsigned_library_type T>
constexpr auto remove_trailing_zeros(const T n)
{
using underlying = typename detail::underlying_type_t<T>;

if (static_cast<underlying>(n) == static_cast<underlying>(0))
{
return detail::remove_trailing_zeros_return<underlying>{static_cast<underlying>(0), static_cast<std::size_t>(0)};
}

return detail::remove_trailing_zeros(static_cast<underlying>(n));
}

template <detail::non_bounded_unsigned_library_type T>
consteval auto remove_trailing_zeros(const detail::verified_type_basis<T> val)
{
using underlying = typename detail::underlying_type_t<T>;

if (static_cast<underlying>(val) == static_cast<underlying>(0))
{
return detail::remove_trailing_zeros_return<underlying>{static_cast<underlying>(0), static_cast<std::size_t>(0)};
}

return detail::remove_trailing_zeros(static_cast<underlying>(val));
}

Expand All @@ -80,6 +93,19 @@ consteval auto is_power_10(const detail::verified_type_basis<T> n) -> bool
return trimmed_number == static_cast<underlying>(1);
}

template <detail::non_bounded_unsigned_library_type T>
requires (!detail::is_verified_type_v<T>)
constexpr auto is_power_2(const T n) noexcept -> bool
{
return has_single_bit(n);
}

template <detail::non_bounded_unsigned_library_type T>
consteval auto is_power_2(const detail::verified_type_basis<T> n) noexcept -> bool
{
return has_single_bit(n);
}

} // namespace boost::safe_numbers

#endif // BOOST_SAFE_NUMBERS_INTEGER_UTILITIES_HPP
1 change: 1 addition & 0 deletions test/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ run test_verified_limits.cpp ;
run test_isqrt.cpp ;
run test_remove_trailing_zeros.cpp ;
run test_is_power_10.cpp ;
run test_is_power_2.cpp ;

# Compile Tests
compile compile_tests/compile_test_unsigned_integers.cpp ;
Expand Down
33 changes: 33 additions & 0 deletions test/test_is_power_10.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,41 @@ void test_is_power_10_verified()
static_assert(!is_power_10(verified_u128{u128{uint128_t{UINT64_C(100001)}}}));
}

// =============================================================================
// Zero input tests
// =============================================================================

template <typename T>
void test_is_power_10_zero()
{
using underlying = typename detail::underlying_type_t<T>;
BOOST_TEST(!is_power_10(T{static_cast<underlying>(0)}));
}

void test_is_power_10_zero_constexpr()
{
static_assert(!is_power_10(u8{static_cast<std::uint8_t>(0)}));
static_assert(!is_power_10(u32{UINT32_C(0)}));
static_assert(!is_power_10(u64{UINT64_C(0)}));
}

void test_is_power_10_zero_verified()
{
static_assert(!is_power_10(verified_u8{u8{static_cast<std::uint8_t>(0)}}));
static_assert(!is_power_10(verified_u32{u32{UINT32_C(0)}}));
}

int main()
{
// Zero input - all types
test_is_power_10_zero<u8>();
test_is_power_10_zero<u16>();
test_is_power_10_zero<u32>();
test_is_power_10_zero<u64>();
test_is_power_10_zero<u128>();
test_is_power_10_zero_constexpr();
test_is_power_10_zero_verified();

// Powers of 10 - all types
test_is_power_10_true<u8>();
test_is_power_10_true<u16>();
Expand Down
Loading