From 8a2e376689a34db10d231a9991494080b39fac7a Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 6 Apr 2020 12:11:54 +0200 Subject: [PATCH 1/2] Add Integer::{log,log2, log10} --- src/libcore/num/mod.rs | 220 +++++++++++++++++++++++++++++++ src/libcore/tests/lib.rs | 1 + src/libcore/tests/num/int_log.rs | 64 +++++++++ src/libcore/tests/num/mod.rs | 1 + 4 files changed, 286 insertions(+) create mode 100644 src/libcore/tests/num/int_log.rs diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index f4a1afd436adb..adc55e628ae0b 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -2160,6 +2160,117 @@ 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 zero, or if the base is zero or one. + +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 lower than 1. + +# 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 lower than 1. + +# 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 +4280,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 negative or zero, or if the base is negative, zero, or one. + +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 lower than 1. + +# 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 lower than 1. + +# 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..05f24b8d75ea3 --- /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).checked_log(13.0) as i16)); + } + for i in 1..=u16::MAX { + assert_eq!(i.checked_log(13), Some((i as f32).checked_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).checked_log2() as u8)); + } + for i in 1..=u16::MAX { + assert_eq!(i.checked_log2(), Some((i as f32).checked_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).checked_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).checked_log2() as i16)); + } +} + +#[test] +fn checked_log10() { + // 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).checked_log10() as i16)); + } + for i in 1..=u16::MAX { + assert_eq!(i.checked_log10(), Some((i as f32).checked_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 { From 6cef103a50d6fdfa068860606ffbea7f7741c8ff Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 5 Jul 2020 21:58:09 -0700 Subject: [PATCH 2/2] Touch up checked_log PR --- src/libcore/num/mod.rs | 14 ++++++-------- src/libcore/tests/num/int_log.rs | 18 +++++++++--------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index adc55e628ae0b..c073a6ca04e47 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -2163,7 +2163,7 @@ 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 zero, or if the base is zero or one. +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 @@ -2214,7 +2214,7 @@ assert_eq!(result, Some(1)); doc_comment! { concat!("Returns the base 2 logarithm of the number. -Returns `None` if the number is lower than 1. +Returns `None` if the number is negative or zero. # Examples @@ -2246,7 +2246,7 @@ assert_eq!(result, Some(1)); doc_comment! { concat!("Returns the base 10 logarithm of the number. -Returns `None` if the number is lower than 1. +Returns `None` if the number is negative or zero. # Examples @@ -2269,8 +2269,6 @@ assert_eq!(result, Some(1)); } } - - doc_comment! { concat!("Computes the absolute value of `self`. @@ -4283,7 +4281,7 @@ 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 negative or zero, or if the base is negative, zero, or one. +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 @@ -4334,7 +4332,7 @@ assert_eq!(result, Some(1)); doc_comment! { concat!("Returns the base 2 logarithm of the number. -Returns `None` if the number is lower than 1. +Returns `None` if the number is zero. # Examples @@ -4366,7 +4364,7 @@ assert_eq!(result, Some(1)); doc_comment! { concat!("Returns the base 10 logarithm of the number. -Returns `None` if the number is lower than 1. +Returns `None` if the number is zero. # Examples diff --git a/src/libcore/tests/num/int_log.rs b/src/libcore/tests/num/int_log.rs index 05f24b8d75ea3..12557641d8a20 100644 --- a/src/libcore/tests/num/int_log.rs +++ b/src/libcore/tests/num/int_log.rs @@ -16,12 +16,13 @@ fn checked_log() { assert_eq!(i.checked_log(4), None); } for i in 1..=i16::MAX { - assert_eq!(i.checked_log(13), Some((i as f32).checked_log(13.0) as i16)); + 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).checked_log(13.0) as u16)); + 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)); @@ -30,35 +31,34 @@ fn checked_log2() { assert_eq!((-55i16).checked_log2(), None); for i in 1..=u8::MAX { - assert_eq!(i.checked_log2(), Some((i as f32).checked_log2() as u8)); + 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).checked_log2() as u16)); + 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).checked_log2() as i8)); + 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).checked_log2() as i16)); + assert_eq!(i.checked_log2(), Some((i as f32).log2() as i16)); } } #[test] fn checked_log10() { - // 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).checked_log10() as i16)); + 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).checked_log10() as u16)); + assert_eq!(i.checked_log10(), Some((i as f32).log10() as u16)); } }