diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index f4a1afd436adb..c073a6ca04e47 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -2160,6 +2160,115 @@ assert_eq!((-a).rem_euclid(-b), 1); } } + doc_comment! { + concat!("Returns the logarithm of the number with respect to an arbitrary base. + +Returns `None` if the number is negative or zero, or if the base is not at least 2. + +This method may not be optimized owing to implementation details; +`self.checked_log2()` can produce results more efficiently for base 2, and +`self.checked_log10()` can produce results more efficiently for base 10. + +# Examples + +``` +#![feature(int_log)] + +let five = 5", stringify!($SelfT), "; + +// log5(5) == 1 +let result = five.checked_log(5); + +assert_eq!(result, Some(1)); +```"), + #[unstable(feature = "int_log", issue = "70887")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn checked_log(self, base: Self) -> Option { + // SAFETY: We check the input to this is always positive + let logb2 = |x: Self| unsafe { intrinsics::ctlz_nonzero(1 as Self) - intrinsics::ctlz_nonzero(x) }; + + if self <= 0 || base <= 1 { + None + } else { + let mut n = 0; + let mut r = self; + + // Optimization for 128 bit wide integers. + if mem::size_of::() * 8 == 128 { + let b = logb2(self) / (logb2(base) + 1); + n += b; + r /= base.pow(b as u32); + } + + while r >= base { + r /= base; + n += 1; + } + Some(n) + } + } + } + + doc_comment! { + concat!("Returns the base 2 logarithm of the number. + +Returns `None` if the number is negative or zero. + +# Examples + +``` +#![feature(int_log)] + +let two = 2", stringify!($SelfT), "; + +// checked_log2(2) == 1 +let result = two.checked_log2(); + +assert_eq!(result, Some(1)); +```"), + #[unstable(feature = "int_log", issue = "70887")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn checked_log2(self) -> Option { + if self <= 0 { + None + } else { + // SAFETY: We just checked that this number is positive + let log = unsafe { intrinsics::ctlz_nonzero(1 as Self) - intrinsics::ctlz_nonzero(self) }; + Some(log) + } + } + } + + doc_comment! { + concat!("Returns the base 10 logarithm of the number. + +Returns `None` if the number is negative or zero. + +# Examples + +``` +#![feature(int_log)] + +let ten = 10", stringify!($SelfT), "; + +// checked_log10(10) == 1 +let result = ten.checked_log10(); + +assert_eq!(result, Some(1)); +```"), + #[unstable(feature = "int_log", issue = "70887")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn checked_log10(self) -> Option { + self.checked_log(10) + } + } + doc_comment! { concat!("Computes the absolute value of `self`. @@ -4169,6 +4278,115 @@ assert_eq!(7", stringify!($SelfT), ".rem_euclid(4), 3); // or any other integer } } + doc_comment! { + concat!("Returns the logarithm of the number with respect to an arbitrary base. + +Returns `None` if the number is zero, or if the base is not at least 2. + +This method may not be optimized owing to implementation details; +`self.checked_log2()` can produce results more efficiently for base 2, and +`self.checked_log10()` can produce results more efficiently for base 10. + +# Examples + +``` +#![feature(int_log)] + +let five = 5", stringify!($SelfT), "; + +// log5(5) == 1 +let result = five.checked_log(5); + +assert_eq!(result, Some(1)); +```"), + #[unstable(feature = "int_log", issue = "70887")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn checked_log(self, base: Self) -> Option { + // SAFETY: We check the input to this is always positive. + let logb2 = |x: Self| unsafe { intrinsics::ctlz_nonzero(1 as Self) - intrinsics::ctlz_nonzero(x) }; + + if self <= 0 || base <= 1 { + None + } else { + let mut n = 0; + let mut r = self; + + // Optimization for 128 bit wide integers. + if mem::size_of::() * 8 == 128 { + let b = logb2(self) / (logb2(base) + 1); + n += b; + r /= base.pow(b as u32); + } + + while r >= base { + r /= base; + n += 1; + } + Some(n) + } + } + } + + doc_comment! { + concat!("Returns the base 2 logarithm of the number. + +Returns `None` if the number is zero. + +# Examples + +``` +#![feature(int_log)] + +let two = 2", stringify!($SelfT), "; + +// checked_log2(2) == 1 +let result = two.checked_log2(); + +assert_eq!(result, Some(1)); +```"), + #[unstable(feature = "int_log", issue = "70887")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn checked_log2(self) -> Option { + if self <= 0 { + None + } else { + // SAFETY: We just checked that this number is positive + let log = unsafe { intrinsics::ctlz_nonzero(1 as Self) - intrinsics::ctlz_nonzero(self) }; + Some(log) + } + } + } + + doc_comment! { + concat!("Returns the base 10 logarithm of the number. + +Returns `None` if the number is zero. + +# Examples + +``` +#![feature(int_log)] + +let ten = 10", stringify!($SelfT), "; + +// checked_log10(10) == 1 +let result = ten.checked_log10(); + +assert_eq!(result, Some(1)); +```"), + #[unstable(feature = "int_log", issue = "70887")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn checked_log10(self) -> Option { + self.checked_log(10) + } + } + doc_comment! { concat!("Returns `true` if and only if `self == 2^k` for some `k`. diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 68a5e20a66fdc..16eaabf1e1fa8 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -13,6 +13,7 @@ #![feature(fmt_internals)] #![feature(hashmap_internals)] #![feature(try_find)] +#![feature(int_log)] #![feature(is_sorted)] #![feature(pattern)] #![feature(range_is_empty)] diff --git a/src/libcore/tests/num/int_log.rs b/src/libcore/tests/num/int_log.rs new file mode 100644 index 0000000000000..12557641d8a20 --- /dev/null +++ b/src/libcore/tests/num/int_log.rs @@ -0,0 +1,64 @@ +#[test] +fn checked_log() { + assert_eq!(999u32.checked_log(10), Some(2)); + assert_eq!(1000u32.checked_log(10), Some(3)); + assert_eq!(555u32.checked_log(13), Some(2)); + assert_eq!(63u32.checked_log(4), Some(2)); + assert_eq!(64u32.checked_log(4), Some(3)); + assert_eq!(10460353203u64.checked_log(3), Some(21)); + assert_eq!(10460353202u64.checked_log(3), Some(20)); + assert_eq!(147808829414345923316083210206383297601u128.checked_log(3), Some(80)); + assert_eq!(147808829414345923316083210206383297600u128.checked_log(3), Some(79)); + assert_eq!(22528399544939174411840147874772641u128.checked_log(19683), Some(8)); + assert_eq!(22528399544939174411840147874772631i128.checked_log(19683), Some(7)); + + for i in i16::MIN..=0 { + assert_eq!(i.checked_log(4), None); + } + for i in 1..=i16::MAX { + assert_eq!(i.checked_log(13), Some((i as f32).log(13.0) as i16)); + } + for i in 1..=u16::MAX { + assert_eq!(i.checked_log(13), Some((i as f32).log(13.0) as u16)); + } +} + +#[test] +fn checked_log2() { + assert_eq!(5u32.checked_log2(), Some(2)); + assert_eq!(0u64.checked_log2(), None); + assert_eq!(128i32.checked_log2(), Some(7)); + assert_eq!((-55i16).checked_log2(), None); + + for i in 1..=u8::MAX { + assert_eq!(i.checked_log2(), Some((i as f32).log2() as u8)); + } + for i in 1..=u16::MAX { + assert_eq!(i.checked_log2(), Some((i as f32).log2() as u16)); + } + for i in i8::MIN..=0 { + assert_eq!(i.checked_log2(), None); + } + for i in 1..=i8::MAX { + assert_eq!(i.checked_log2(), Some((i as f32).log2() as i8)); + } + for i in i16::MIN..=0 { + assert_eq!(i.checked_log2(), None); + } + for i in 1..=i16::MAX { + assert_eq!(i.checked_log2(), Some((i as f32).log2() as i16)); + } +} + +#[test] +fn checked_log10() { + for i in i16::MIN..=0 { + assert_eq!(i.checked_log10(), None); + } + for i in 1..=i16::MAX { + assert_eq!(i.checked_log10(), Some((i as f32).log10() as i16)); + } + for i in 1..=u16::MAX { + assert_eq!(i.checked_log10(), Some((i as f32).log10() as u16)); + } +} diff --git a/src/libcore/tests/num/mod.rs b/src/libcore/tests/num/mod.rs index 939f1325c8499..51f67c363f84a 100644 --- a/src/libcore/tests/num/mod.rs +++ b/src/libcore/tests/num/mod.rs @@ -26,6 +26,7 @@ mod u8; mod bignum; mod dec2flt; mod flt2dec; +mod int_log; /// Adds the attribute to all items in the block. macro_rules! cfg_block {