Skip to content

Commit 0c81bab

Browse files
bidzyyys0xNeshiimmrsd
authored
refactor: replace is_power_of_ten lookup tables with log10_floor + pow macro (#323)
* refactor: replace is_power_of_ten lookup tables with log10_floor + pow macro Closes #295. Public API unchanged. The new public(package) macro `is_power_of_ten!` in internal/macros.move computes the predicate via the existing log10_floor and std::u256::pow primitives, eliminating the duplicated power-of-ten constants that previously caused the missing 10^77 entry (fixed in #291). Only u128 and u256 wrappers are retargeted per the team's gas-report decision; u8 through u64 keep their inline disjunctions. * style: apply prettier-move and drop INV-N comment markers * chore: drop CHANGELOG entry for is_power_of_ten refactor * ref: use receiver syntax for u256.pow in is_power_of_ten macro * ref: apply suggestion from @0xNeshi Co-authored-by: Nenad <nenad.misic@openzeppelin.com> * fix: fmt * docs: update docs * chore: test cleanup * test: cleanup * Remove the orphaned "binary_search" internal helper macro and its tests * Add more test cases * Format files --------- Co-authored-by: Nenad <nenad.misic@openzeppelin.com> Co-authored-by: immrsd <immrsd.eth@gmail.com>
1 parent 4c036d1 commit 0c81bab

8 files changed

Lines changed: 220 additions & 414 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
2121
- `sqrt` for `UD30x9` and `SD29x9` with round-down semantics. (#286)
2222
- `log2`, `ln`, `log10` for `UD30x9` (rounds down) and `SD29x9` (rounds toward zero). (#320)
2323

24+
### `openzeppelin_math`
25+
26+
#### Changed
27+
28+
- `u128::is_power_of_ten` and `u256::is_power_of_ten` now compute the result via `log10_floor` and `pow` instead of a hardcoded lookup table. (#323)
29+
2430
## 1.1.0 (21-04-2026)
2531

2632
### `openzeppelin_fp_math`

math/core/sources/internal/macros.move

Lines changed: 20 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,26 @@ public(package) macro fun log10<$Int>($value: $Int, $rounding_mode: RoundingMode
404404
}
405405
}
406406

407+
/// Test whether `$n` is an exact non-negative integer power of ten (`10^k` for some
408+
/// `k >= 0`).
409+
///
410+
/// #### Generics
411+
/// - `$Int`: Any unsigned integer type (`u8`, `u16`, `u32`, `u64`, `u128`, or `u256`).
412+
///
413+
/// #### Parameters
414+
/// - `$n`: The unsigned integer to test.
415+
///
416+
/// #### Returns
417+
/// - `true` if `$n` equals `10^k` for some `k >= 0`, otherwise `false`.
418+
public(package) macro fun is_power_of_ten<$Int>($n: $Int): bool {
419+
let n = $n as u256;
420+
if (n == 0) {
421+
return false
422+
};
423+
let exp = log10_floor(n);
424+
n == 10u256.pow(exp)
425+
}
426+
407427
/// Compute floor(log10(value)) using binary search over powers of 10.
408428
///
409429
/// This helper uses precomputed constants (`TEN_POW_2`, `TEN_POW_4`, etc.) to efficiently
@@ -1015,41 +1035,3 @@ public(package) fun mul_mod_impl(a: u256, b: u256, modulus: u256): u256 {
10151035
((a * b) % modulus)
10161036
}
10171037
}
1018-
1019-
/// Perform binary search on a sorted vector to find if `$needle` exists in the vector.
1020-
///
1021-
/// The helper works across all unsigned widths and performs a standard iterative binary
1022-
/// search on a sorted vector. It compares the search value against the middle element
1023-
/// at each iteration until either the value is found or the search space is exhausted.
1024-
///
1025-
/// #### Generics
1026-
/// - `$Int`: Any unsigned integer type (`u8`, `u16`, `u32`, `u64`, `u128`, or `u256`).
1027-
///
1028-
/// #### Parameters
1029-
/// - `$haystack`: A sorted vector of unsigned integers to search through.
1030-
/// - `$needle`: The value to search for in the vector.
1031-
///
1032-
/// #### Returns
1033-
/// `true` if `$needle` exists in `$haystack`, `false` otherwise.
1034-
public(package) macro fun binary_search<$Int>($haystack: vector<$Int>, $needle: $Int): bool {
1035-
let haystack = $haystack;
1036-
let needle = $needle;
1037-
1038-
let mut left = 0;
1039-
let mut right = haystack.length();
1040-
1041-
while (left < right) {
1042-
let mid = left + (right - left) / 2;
1043-
let mid_val = *haystack.borrow(mid);
1044-
1045-
if (mid_val == needle) {
1046-
return true
1047-
} else if (mid_val < needle) {
1048-
left = mid + 1;
1049-
} else {
1050-
right = mid;
1051-
}
1052-
};
1053-
1054-
false
1055-
}

math/core/sources/u128.move

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,6 @@ public fun mul_mod(a: u128, b: u128, modulus: u128): u128 {
217217

218218
/// Returns `true` if `n` is a power of ten.
219219
///
220-
/// Uses a lookup table with binary search for efficiency.
221220
/// For `u128`, valid powers of ten range from 10^0 to 10^38.
222221
///
223222
/// #### Parameters
@@ -226,48 +225,5 @@ public fun mul_mod(a: u128, b: u128, modulus: u128): u128 {
226225
/// #### Returns
227226
/// - `true` if `n` is a power of ten within the `u128` range, otherwise `false`.
228227
public fun is_power_of_ten(n: u128): bool {
229-
// Powers of 10 from 10^0 to 10^38 for u128
230-
let powers = vector[
231-
1u128,
232-
10u128,
233-
100u128,
234-
1000u128,
235-
10000u128,
236-
100000u128,
237-
1000000u128,
238-
10000000u128,
239-
100000000u128,
240-
1000000000u128,
241-
10000000000u128,
242-
100000000000u128,
243-
1000000000000u128,
244-
10000000000000u128,
245-
100000000000000u128,
246-
1000000000000000u128,
247-
10000000000000000u128,
248-
100000000000000000u128,
249-
1000000000000000000u128,
250-
10000000000000000000u128,
251-
100000000000000000000u128,
252-
1000000000000000000000u128,
253-
10000000000000000000000u128,
254-
100000000000000000000000u128,
255-
1000000000000000000000000u128,
256-
10000000000000000000000000u128,
257-
100000000000000000000000000u128,
258-
1000000000000000000000000000u128,
259-
10000000000000000000000000000u128,
260-
100000000000000000000000000000u128,
261-
1000000000000000000000000000000u128,
262-
10000000000000000000000000000000u128,
263-
100000000000000000000000000000000u128,
264-
1000000000000000000000000000000000u128,
265-
10000000000000000000000000000000000u128,
266-
100000000000000000000000000000000000u128,
267-
1000000000000000000000000000000000000u128,
268-
10000000000000000000000000000000000000u128,
269-
100000000000000000000000000000000000000u128,
270-
];
271-
272-
macros::binary_search!(powers, n)
228+
macros::is_power_of_ten!(n)
273229
}

math/core/sources/u256.move

Lines changed: 1 addition & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,6 @@ public fun mul_mod(a: u256, b: u256, modulus: u256): u256 {
226226

227227
/// Returns `true` if `n` is a power of ten.
228228
///
229-
/// Uses a lookup table with binary search for efficiency.
230229
/// For `u256`, valid powers of ten range from 10^0 to 10^77.
231230
///
232231
/// #### Parameters
@@ -235,87 +234,5 @@ public fun mul_mod(a: u256, b: u256, modulus: u256): u256 {
235234
/// #### Returns
236235
/// - `true` if `n` is a power of ten within the `u256` range, otherwise `false`.
237236
public fun is_power_of_ten(n: u256): bool {
238-
// Powers of 10 from 10^0 to 10^77 for u256
239-
let powers = vector[
240-
1u256,
241-
10u256,
242-
100u256,
243-
1000u256,
244-
10000u256,
245-
100000u256,
246-
1000000u256,
247-
10000000u256,
248-
100000000u256,
249-
1000000000u256,
250-
10000000000u256,
251-
100000000000u256,
252-
1000000000000u256,
253-
10000000000000u256,
254-
100000000000000u256,
255-
1000000000000000u256,
256-
10000000000000000u256,
257-
100000000000000000u256,
258-
1000000000000000000u256,
259-
10000000000000000000u256,
260-
100000000000000000000u256,
261-
1000000000000000000000u256,
262-
10000000000000000000000u256,
263-
100000000000000000000000u256,
264-
1000000000000000000000000u256,
265-
10000000000000000000000000u256,
266-
100000000000000000000000000u256,
267-
1000000000000000000000000000u256,
268-
10000000000000000000000000000u256,
269-
100000000000000000000000000000u256,
270-
1000000000000000000000000000000u256,
271-
10000000000000000000000000000000u256,
272-
100000000000000000000000000000000u256,
273-
1000000000000000000000000000000000u256,
274-
10000000000000000000000000000000000u256,
275-
100000000000000000000000000000000000u256,
276-
1000000000000000000000000000000000000u256,
277-
10000000000000000000000000000000000000u256,
278-
100000000000000000000000000000000000000u256,
279-
1000000000000000000000000000000000000000u256,
280-
10000000000000000000000000000000000000000u256,
281-
100000000000000000000000000000000000000000u256,
282-
1000000000000000000000000000000000000000000u256,
283-
10000000000000000000000000000000000000000000u256,
284-
100000000000000000000000000000000000000000000u256,
285-
1000000000000000000000000000000000000000000000u256,
286-
10000000000000000000000000000000000000000000000u256,
287-
100000000000000000000000000000000000000000000000u256,
288-
1000000000000000000000000000000000000000000000000u256,
289-
10000000000000000000000000000000000000000000000000u256,
290-
100000000000000000000000000000000000000000000000000u256,
291-
1000000000000000000000000000000000000000000000000000u256,
292-
10000000000000000000000000000000000000000000000000000u256,
293-
100000000000000000000000000000000000000000000000000000u256,
294-
1000000000000000000000000000000000000000000000000000000u256,
295-
10000000000000000000000000000000000000000000000000000000u256,
296-
100000000000000000000000000000000000000000000000000000000u256,
297-
1000000000000000000000000000000000000000000000000000000000u256,
298-
10000000000000000000000000000000000000000000000000000000000u256,
299-
100000000000000000000000000000000000000000000000000000000000u256,
300-
1000000000000000000000000000000000000000000000000000000000000u256,
301-
10000000000000000000000000000000000000000000000000000000000000u256,
302-
100000000000000000000000000000000000000000000000000000000000000u256,
303-
1000000000000000000000000000000000000000000000000000000000000000u256,
304-
10000000000000000000000000000000000000000000000000000000000000000u256,
305-
100000000000000000000000000000000000000000000000000000000000000000u256,
306-
1000000000000000000000000000000000000000000000000000000000000000000u256,
307-
10000000000000000000000000000000000000000000000000000000000000000000u256,
308-
100000000000000000000000000000000000000000000000000000000000000000000u256,
309-
1000000000000000000000000000000000000000000000000000000000000000000000u256,
310-
10000000000000000000000000000000000000000000000000000000000000000000000u256,
311-
100000000000000000000000000000000000000000000000000000000000000000000000u256,
312-
1000000000000000000000000000000000000000000000000000000000000000000000000u256,
313-
10000000000000000000000000000000000000000000000000000000000000000000000000u256,
314-
100000000000000000000000000000000000000000000000000000000000000000000000000u256,
315-
1000000000000000000000000000000000000000000000000000000000000000000000000000u256,
316-
10000000000000000000000000000000000000000000000000000000000000000000000000000u256,
317-
100000000000000000000000000000000000000000000000000000000000000000000000000000u256,
318-
];
319-
320-
macros::binary_search!(powers, n)
237+
macros::is_power_of_ten!(n)
321238
}

0 commit comments

Comments
 (0)