Mehdi Akiki
Rust Failure Atlas / Upgrades and compatibility

RFA-417 · Case file with fixtures · Case 389 of 694 · Runtime evidence

Integer ilog Panics When the Base Is Below Two

Integer ilog is a total-looking method with input preconditions: the value must be positive and the base at least two. checked_ilog returns None for invalid value or base, making it the safer boundary for untrusted inputs.

Reviewed
Rust
Rust 1.98.1, edition 2024
Targets
all Rust targets
Profiles
dev, release, test

Direct answer

What this Rust failure means

Why it happens
The total ilog method has preconditions for both a positive value and a valid base, while checked_ilog encodes invalid input as None instead of panicking.
First discriminating check
Treat both value and base as input constraints and use checked_ilog when either can come from data rather than a proven invariant.

I expected ilog to return a small integer for any integer base. Base one has no useful positional logarithm, and Rust treats that input as a violated precondition.

The failing fixture calls 8_u32.ilog(1). Rust 1.98.1 panics before the assertion with “base of integer logarithm must be at least 2.”

ilog answers a floor question

u32::ilog returns the floor of the logarithm of a positive integer in a supplied integer base.

For eight in base two, the answer is three because:

2^3 = 8

For values between powers, the result is the exponent of the largest base power not exceeding the value. This makes ilog useful for digit counts, bucket selection, and magnitude classes without converting to floating point.

Base zero and one are invalid

A positional base needs to be at least two. Powers of one never grow, so no finite greatest exponent describes ordinary magnitude. Base zero is not a valid logarithm base either.

The unchecked-in-name ilog method uses a panic for these invalid inputs. It also panics when the value itself is zero, because logarithm of zero has no finite result.

These panics occur in release builds too. They are method preconditions, not debug-only overflow checks.

checked_ilog handles both input dimensions

u32::checked_ilog returns Option<u32>. On Rust 1.98.1 it returns None for base one and for value zero.

The repaired fixture records all three cases:

assert_eq!(8_u32.checked_ilog(1), None);
assert_eq!(8_u32.checked_ilog(2), Some(3));
assert_eq!(0_u32.checked_ilog(2), None);

This was important evidence because I initially expected the checked method to validate only the value. Running the fixture showed that it safely covers the invalid base too.

None combines two causes

The Option result does not say whether the value was zero or the base was below two. If a user-facing error needs that distinction, I validate inputs explicitly and return a domain enum:

InvalidValue
InvalidBase
Valid(exponent)

If both conditions share one fallback policy, the compact checked method is enough.

I do not recover the cause by retrying operations or parsing panic text.

ilog2 has a simpler contract

u32::ilog2 fixes the base at two, removing one invalid input dimension. It still requires a positive value.

checked_ilog2 returns None for zero. When the base is a compile-time constant two, this method states intent more clearly and may map well to bit operations.

For powers of ten, ilog10 and its checked form similarly avoid passing a runtime base.

Digit counts need an added one

For a positive integer, floor log in the representation base is one less than the number of digits. Decimal 999 has ilog10() == 2 but three digits.

I write the formula explicitly and test powers around the boundary:

digits = ilog(base) + 1

Zero needs a separate policy because humans normally write it with one digit even though its logarithm is undefined.

Avoid floating conversion for exact integer boundaries

Converting a large integer to f64 and calling a floating logarithm can round near exact powers. Integer ilog provides exact integer semantics for the supported type range.

This is useful in serializers, radix formatting, and allocation estimates. I still use checked arithmetic when turning an exponent into a size, because later multiplication or exponentiation can overflow independently.

External bases are untrusted resource inputs

A base may arrive in configuration, a query parameter, or file metadata. I parse it into an unsigned integer, enforce the supported range, and use checked_ilog.

Validation also protects later code that allocates digit buffers or builds lookup tables based on the base. Avoiding one panic is only the first layer of a complete resource policy.

My boundary table

I test:

  • value zero with valid base;
  • value one with valid base;
  • base zero and one;
  • base two;
  • values just below, at, and above exact powers;
  • the maximum integer value;
  • any domain upper bound on the base.

The core principle is that a checked API should be chosen at data boundaries even when the result type looks less convenient. ilog is concise after invariants prove positive value and base at least two. checked_ilog makes those invalid states explicit and keeps malformed input out of the panic path.