Skip to content

Commit 8a2e376

Browse files
committed
Add Integer::{log,log2, log10}
1 parent 0cd7ff7 commit 8a2e376

File tree

4 files changed

+286
-0
lines changed

4 files changed

+286
-0
lines changed

src/libcore/num/mod.rs

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2160,6 +2160,117 @@ assert_eq!((-a).rem_euclid(-b), 1);
21602160
}
21612161
}
21622162

2163+
doc_comment! {
2164+
concat!("Returns the logarithm of the number with respect to an arbitrary base.
2165+
2166+
Returns `None` if the number is zero, or if the base is zero or one.
2167+
2168+
This method may not be optimized owing to implementation details;
2169+
`self.checked_log2()` can produce results more efficiently for base 2, and
2170+
`self.checked_log10()` can produce results more efficiently for base 10.
2171+
2172+
# Examples
2173+
2174+
```
2175+
#![feature(int_log)]
2176+
2177+
let five = 5", stringify!($SelfT), ";
2178+
2179+
// log5(5) == 1
2180+
let result = five.checked_log(5);
2181+
2182+
assert_eq!(result, Some(1));
2183+
```"),
2184+
#[unstable(feature = "int_log", issue = "70887")]
2185+
#[must_use = "this returns the result of the operation, \
2186+
without modifying the original"]
2187+
#[inline]
2188+
pub fn checked_log(self, base: Self) -> Option<Self> {
2189+
// SAFETY: We check the input to this is always positive
2190+
let logb2 = |x: Self| unsafe { intrinsics::ctlz_nonzero(1 as Self) - intrinsics::ctlz_nonzero(x) };
2191+
2192+
if self <= 0 || base <= 1 {
2193+
None
2194+
} else {
2195+
let mut n = 0;
2196+
let mut r = self;
2197+
2198+
// Optimization for 128 bit wide integers.
2199+
if mem::size_of::<Self>() * 8 == 128 {
2200+
let b = logb2(self) / (logb2(base) + 1);
2201+
n += b;
2202+
r /= base.pow(b as u32);
2203+
}
2204+
2205+
while r >= base {
2206+
r /= base;
2207+
n += 1;
2208+
}
2209+
Some(n)
2210+
}
2211+
}
2212+
}
2213+
2214+
doc_comment! {
2215+
concat!("Returns the base 2 logarithm of the number.
2216+
2217+
Returns `None` if the number is lower than 1.
2218+
2219+
# Examples
2220+
2221+
```
2222+
#![feature(int_log)]
2223+
2224+
let two = 2", stringify!($SelfT), ";
2225+
2226+
// checked_log2(2) == 1
2227+
let result = two.checked_log2();
2228+
2229+
assert_eq!(result, Some(1));
2230+
```"),
2231+
#[unstable(feature = "int_log", issue = "70887")]
2232+
#[must_use = "this returns the result of the operation, \
2233+
without modifying the original"]
2234+
#[inline]
2235+
pub fn checked_log2(self) -> Option<Self> {
2236+
if self <= 0 {
2237+
None
2238+
} else {
2239+
// SAFETY: We just checked that this number is positive
2240+
let log = unsafe { intrinsics::ctlz_nonzero(1 as Self) - intrinsics::ctlz_nonzero(self) };
2241+
Some(log)
2242+
}
2243+
}
2244+
}
2245+
2246+
doc_comment! {
2247+
concat!("Returns the base 10 logarithm of the number.
2248+
2249+
Returns `None` if the number is lower than 1.
2250+
2251+
# Examples
2252+
2253+
```
2254+
#![feature(int_log)]
2255+
2256+
let ten = 10", stringify!($SelfT), ";
2257+
2258+
// checked_log10(10) == 1
2259+
let result = ten.checked_log10();
2260+
2261+
assert_eq!(result, Some(1));
2262+
```"),
2263+
#[unstable(feature = "int_log", issue = "70887")]
2264+
#[must_use = "this returns the result of the operation, \
2265+
without modifying the original"]
2266+
#[inline]
2267+
pub fn checked_log10(self) -> Option<Self> {
2268+
self.checked_log(10)
2269+
}
2270+
}
2271+
2272+
2273+
21632274
doc_comment! {
21642275
concat!("Computes the absolute value of `self`.
21652276
@@ -4169,6 +4280,115 @@ assert_eq!(7", stringify!($SelfT), ".rem_euclid(4), 3); // or any other integer
41694280
}
41704281
}
41714282

4283+
doc_comment! {
4284+
concat!("Returns the logarithm of the number with respect to an arbitrary base.
4285+
4286+
Returns `None` if the number is negative or zero, or if the base is negative, zero, or one.
4287+
4288+
This method may not be optimized owing to implementation details;
4289+
`self.checked_log2()` can produce results more efficiently for base 2, and
4290+
`self.checked_log10()` can produce results more efficiently for base 10.
4291+
4292+
# Examples
4293+
4294+
```
4295+
#![feature(int_log)]
4296+
4297+
let five = 5", stringify!($SelfT), ";
4298+
4299+
// log5(5) == 1
4300+
let result = five.checked_log(5);
4301+
4302+
assert_eq!(result, Some(1));
4303+
```"),
4304+
#[unstable(feature = "int_log", issue = "70887")]
4305+
#[must_use = "this returns the result of the operation, \
4306+
without modifying the original"]
4307+
#[inline]
4308+
pub fn checked_log(self, base: Self) -> Option<Self> {
4309+
// SAFETY: We check the input to this is always positive.
4310+
let logb2 = |x: Self| unsafe { intrinsics::ctlz_nonzero(1 as Self) - intrinsics::ctlz_nonzero(x) };
4311+
4312+
if self <= 0 || base <= 1 {
4313+
None
4314+
} else {
4315+
let mut n = 0;
4316+
let mut r = self;
4317+
4318+
// Optimization for 128 bit wide integers.
4319+
if mem::size_of::<Self>() * 8 == 128 {
4320+
let b = logb2(self) / (logb2(base) + 1);
4321+
n += b;
4322+
r /= base.pow(b as u32);
4323+
}
4324+
4325+
while r >= base {
4326+
r /= base;
4327+
n += 1;
4328+
}
4329+
Some(n)
4330+
}
4331+
}
4332+
}
4333+
4334+
doc_comment! {
4335+
concat!("Returns the base 2 logarithm of the number.
4336+
4337+
Returns `None` if the number is lower than 1.
4338+
4339+
# Examples
4340+
4341+
```
4342+
#![feature(int_log)]
4343+
4344+
let two = 2", stringify!($SelfT), ";
4345+
4346+
// checked_log2(2) == 1
4347+
let result = two.checked_log2();
4348+
4349+
assert_eq!(result, Some(1));
4350+
```"),
4351+
#[unstable(feature = "int_log", issue = "70887")]
4352+
#[must_use = "this returns the result of the operation, \
4353+
without modifying the original"]
4354+
#[inline]
4355+
pub fn checked_log2(self) -> Option<Self> {
4356+
if self <= 0 {
4357+
None
4358+
} else {
4359+
// SAFETY: We just checked that this number is positive
4360+
let log = unsafe { intrinsics::ctlz_nonzero(1 as Self) - intrinsics::ctlz_nonzero(self) };
4361+
Some(log)
4362+
}
4363+
}
4364+
}
4365+
4366+
doc_comment! {
4367+
concat!("Returns the base 10 logarithm of the number.
4368+
4369+
Returns `None` if the number is lower than 1.
4370+
4371+
# Examples
4372+
4373+
```
4374+
#![feature(int_log)]
4375+
4376+
let ten = 10", stringify!($SelfT), ";
4377+
4378+
// checked_log10(10) == 1
4379+
let result = ten.checked_log10();
4380+
4381+
assert_eq!(result, Some(1));
4382+
```"),
4383+
#[unstable(feature = "int_log", issue = "70887")]
4384+
#[must_use = "this returns the result of the operation, \
4385+
without modifying the original"]
4386+
#[inline]
4387+
pub fn checked_log10(self) -> Option<Self> {
4388+
self.checked_log(10)
4389+
}
4390+
}
4391+
41724392
doc_comment! {
41734393
concat!("Returns `true` if and only if `self == 2^k` for some `k`.
41744394

src/libcore/tests/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#![feature(fmt_internals)]
1414
#![feature(hashmap_internals)]
1515
#![feature(try_find)]
16+
#![feature(int_log)]
1617
#![feature(is_sorted)]
1718
#![feature(pattern)]
1819
#![feature(range_is_empty)]

src/libcore/tests/num/int_log.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#[test]
2+
fn checked_log() {
3+
assert_eq!(999u32.checked_log(10), Some(2));
4+
assert_eq!(1000u32.checked_log(10), Some(3));
5+
assert_eq!(555u32.checked_log(13), Some(2));
6+
assert_eq!(63u32.checked_log(4), Some(2));
7+
assert_eq!(64u32.checked_log(4), Some(3));
8+
assert_eq!(10460353203u64.checked_log(3), Some(21));
9+
assert_eq!(10460353202u64.checked_log(3), Some(20));
10+
assert_eq!(147808829414345923316083210206383297601u128.checked_log(3), Some(80));
11+
assert_eq!(147808829414345923316083210206383297600u128.checked_log(3), Some(79));
12+
assert_eq!(22528399544939174411840147874772641u128.checked_log(19683), Some(8));
13+
assert_eq!(22528399544939174411840147874772631i128.checked_log(19683), Some(7));
14+
15+
for i in i16::MIN..=0 {
16+
assert_eq!(i.checked_log(4), None);
17+
}
18+
for i in 1..=i16::MAX {
19+
assert_eq!(i.checked_log(13), Some((i as f32).checked_log(13.0) as i16));
20+
}
21+
for i in 1..=u16::MAX {
22+
assert_eq!(i.checked_log(13), Some((i as f32).checked_log(13.0) as u16));
23+
}
24+
}
25+
#[test]
26+
fn checked_log2() {
27+
assert_eq!(5u32.checked_log2(), Some(2));
28+
assert_eq!(0u64.checked_log2(), None);
29+
assert_eq!(128i32.checked_log2(), Some(7));
30+
assert_eq!((-55i16).checked_log2(), None);
31+
32+
for i in 1..=u8::MAX {
33+
assert_eq!(i.checked_log2(), Some((i as f32).checked_log2() as u8));
34+
}
35+
for i in 1..=u16::MAX {
36+
assert_eq!(i.checked_log2(), Some((i as f32).checked_log2() as u16));
37+
}
38+
for i in i8::MIN..=0 {
39+
assert_eq!(i.checked_log2(), None);
40+
}
41+
for i in 1..=i8::MAX {
42+
assert_eq!(i.checked_log2(), Some((i as f32).checked_log2() as i8));
43+
}
44+
for i in i16::MIN..=0 {
45+
assert_eq!(i.checked_log2(), None);
46+
}
47+
for i in 1..=i16::MAX {
48+
assert_eq!(i.checked_log2(), Some((i as f32).checked_log2() as i16));
49+
}
50+
}
51+
52+
#[test]
53+
fn checked_log10() {
54+
// checked_log10()
55+
for i in i16::MIN..=0 {
56+
assert_eq!(i.checked_log10(), None);
57+
}
58+
for i in 1..=i16::MAX {
59+
assert_eq!(i.checked_log10(), Some((i as f32).checked_log10() as i16));
60+
}
61+
for i in 1..=u16::MAX {
62+
assert_eq!(i.checked_log10(), Some((i as f32).checked_log10() as u16));
63+
}
64+
}

src/libcore/tests/num/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ mod u8;
2626
mod bignum;
2727
mod dec2flt;
2828
mod flt2dec;
29+
mod int_log;
2930

3031
/// Adds the attribute to all items in the block.
3132
macro_rules! cfg_block {

0 commit comments

Comments
 (0)