- Start Date: 2024-01-08
- RFC PR: amaranth-lang/rfcs#17
- Amaranth Issue: amaranth-lang/amaranth#1025
Remove log2_int
Summary
Replace log2_int
with two functions: ceil_log2
and exact_log2
.
Motivation
log2_int
is a helper function that was copied from Migen in the early days of Amaranth.
It behaves like so:
n
must be an integer, and a power-of-2 unlessneed_pow2
isFalse
;- if
n == 0
, it returns0
; - if
n != 0
, it returns(n - 1).bit_length()
.
Differences with math.log2
In practice, log2_int
differs from math.log2
in the following ways:
- its implementation is restricted to integers only;
- if
need_pow2
is false, the result is rounded up to the nearest integer; - it doesn't raise an exception for
n == 0
; - if
need_pow2
is false, it doesn't raise an exception forn < 0
.
Observations
- 1) is a desirable property; coercing integers into floating-point numbers is fraught with peril, as the latter have limited precision.
- 2) has common use-cases in digital design, such as address decoders.
- 3) and 4) are misleading at best. Despite being advertised as a logarithm,
log2_int
doesn't exclude 0 or negative integers from its domain.
Guide-level explanation
Amaranth provides two log2 functions for integer arithmetic:
ceil_log2(n)
, wheren
is assumed to be any non-negative integerexact_log2(n)
, wheren
is assumed to be an integer power-of-2
For example:
ceil_log2(8) # 3
ceil_log2(5) # 3
ceil_log2(4) # 2
exact_log2(8) # 3
exact_log2(5) # raises a ValueError
exact_log2(4) # 2
Reference-level explanation
Use of the log2_int
function is deprecated.
A ceil_log2(n)
function is added, that:
- returns the integer log2 of the smallest power-of-2 greater than or equal to
n
; - raises a
TypeError
ifn
is not an integer; - raises a
ValueError
ifn
is lesser than 0.
An exact_log2(n)
function is added, that:
- returns the integer log2 of
n
; - raises a
TypeError
ifn
is not an integer; - raises a
ValueError
ifn
is not a power-of-two.
Drawbacks
This is a breaking change.
Rationale and alternatives
The following alternatives have been considered:
- Do nothing. Callers of
log2_int
may still need to restrict its domain to positive integers. - Restrict
log2_int
to positive integers. Downstream code relying on the previous behavior may silently break. - Remove
log2_int
, and usemath.log2
as replacement:log2_int(n)
would be replaced withmath.log2(n)
log2_int(n, need_pow2=False)
would be replaced withmath.ceil(math.log2(n))
Option 3) will give incorrect results, as n
is coerced from int
to float
:
>>> log2_int((1 << 64) + 1, need_pow2=False)
65
>>> math.ceil(math.log2((1 << 64) + 1))
64
Prior art
None.
Unresolved questions
None.
Future possibilities
None.
Acknowledgements
@wanda-phi provided valuable feedback while this RFC was being drafted.