Add Integer::{log,log2,log10} variants
This commit is contained in:
parent
b6f3cb9502
commit
9f579968cd
@ -1744,6 +1744,194 @@ pub const fn rem_euclid(self, rhs: Self) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the logarithm of the number with respect to an arbitrary base.
|
||||
///
|
||||
/// This method may not be optimized owing to implementation details;
|
||||
/// `log2` can produce results more efficiently for base 2, and `log10`
|
||||
/// can produce results more efficiently for base 10.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// When the number is zero, or if the base is not at least 2; it
|
||||
/// panics in debug mode and the return value is wrapped to 0 in release
|
||||
/// mode (the only situation in which the method can return 0).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(int_log)]
|
||||
#[doc = concat!("assert_eq!(5", stringify!($SelfT), ".log(5), 1);")]
|
||||
/// ```
|
||||
#[unstable(feature = "int_log", issue = "70887")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
#[rustc_inherit_overflow_checks]
|
||||
#[allow(arithmetic_overflow)]
|
||||
pub const fn log(self, base: Self) -> Self {
|
||||
match self.checked_log(base) {
|
||||
Some(n) => n,
|
||||
None => {
|
||||
// In debug builds, trigger a panic on None.
|
||||
// This should optimize completely out in release builds.
|
||||
let _ = Self::MAX + 1;
|
||||
|
||||
0
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the base 2 logarithm of the number.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// When the number is zero it panics in debug mode and the return value
|
||||
/// is wrapped to 0 in release mode (the only situation in which the
|
||||
/// method can return 0).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(int_log)]
|
||||
#[doc = concat!("assert_eq!(2", stringify!($SelfT), ".log2(), 1);")]
|
||||
/// ```
|
||||
#[unstable(feature = "int_log", issue = "70887")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
#[rustc_inherit_overflow_checks]
|
||||
#[allow(arithmetic_overflow)]
|
||||
pub const fn log2(self) -> Self {
|
||||
match self.checked_log2() {
|
||||
Some(n) => n,
|
||||
None => {
|
||||
// In debug builds, trigger a panic on None.
|
||||
// This should optimize completely out in release builds.
|
||||
let _ = Self::MAX + 1;
|
||||
|
||||
0
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the base 10 logarithm of the number.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// When the number is zero it panics in debug mode and the return value
|
||||
/// is wrapped to 0 in release mode (the only situation in which the
|
||||
/// method can return 0).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(int_log)]
|
||||
#[doc = concat!("assert_eq!(10", stringify!($SelfT), ".log10(), 1);")]
|
||||
/// ```
|
||||
#[unstable(feature = "int_log", issue = "70887")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
#[rustc_inherit_overflow_checks]
|
||||
#[allow(arithmetic_overflow)]
|
||||
pub const fn log10(self) -> Self {
|
||||
match self.checked_log10() {
|
||||
Some(n) => n,
|
||||
None => {
|
||||
// In debug builds, trigger a panic on None.
|
||||
// This should optimize completely out in release builds.
|
||||
let _ = Self::MAX + 1;
|
||||
|
||||
0
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// 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;
|
||||
/// `checked_log2` can produce results more efficiently for base 2, and
|
||||
/// `checked_log10` can produce results more efficiently for base 10.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(int_log)]
|
||||
#[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_log(5), Some(1));")]
|
||||
/// ```
|
||||
#[unstable(feature = "int_log", issue = "70887")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline]
|
||||
pub const fn checked_log(self, base: Self) -> Option<Self> {
|
||||
if self <= 0 || base <= 1 {
|
||||
None
|
||||
} else {
|
||||
let mut n = 0;
|
||||
let mut r = self;
|
||||
|
||||
// Optimization for 128 bit wide integers.
|
||||
if Self::BITS == 128 {
|
||||
let b = Self::log2(self) / (Self::log2(base) + 1);
|
||||
n += b;
|
||||
r /= base.pow(b as u32);
|
||||
}
|
||||
|
||||
while r >= base {
|
||||
r /= base;
|
||||
n += 1;
|
||||
}
|
||||
Some(n)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the base 2 logarithm of the number.
|
||||
///
|
||||
/// Returns `None` if the number is negative or zero.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(int_log)]
|
||||
#[doc = concat!("assert_eq!(2", stringify!($SelfT), ".checked_log2(), Some(1));")]
|
||||
/// ```
|
||||
#[unstable(feature = "int_log", issue = "70887")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline]
|
||||
pub const fn checked_log2(self) -> Option<Self> {
|
||||
if self <= 0 {
|
||||
None
|
||||
} else {
|
||||
// SAFETY: We just checked that this number is positive
|
||||
let log = (Self::BITS - 1) as Self - unsafe { intrinsics::ctlz_nonzero(self) };
|
||||
Some(log)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the base 10 logarithm of the number.
|
||||
///
|
||||
/// Returns `None` if the number is negative or zero.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(int_log)]
|
||||
#[doc = concat!("assert_eq!(10", stringify!($SelfT), ".checked_log10(), Some(1));")]
|
||||
/// ```
|
||||
#[unstable(feature = "int_log", issue = "70887")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline]
|
||||
pub const fn checked_log10(self) -> Option<Self> {
|
||||
self.checked_log(10)
|
||||
}
|
||||
|
||||
/// Computes the absolute value of `self`.
|
||||
///
|
||||
/// # Overflow behavior
|
||||
|
@ -634,6 +634,194 @@ pub const fn checked_rem_euclid(self, rhs: Self) -> Option<Self> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the logarithm of the number with respect to an arbitrary base.
|
||||
///
|
||||
/// This method may not be optimized owing to implementation details;
|
||||
/// `log2` can produce results more efficiently for base 2, and `log10`
|
||||
/// can produce results more efficiently for base 10.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// When the number is negative, zero, or if the base is not at least 2;
|
||||
/// it panics in debug mode and the return value is wrapped to 0 in
|
||||
/// release mode (the only situation in which the method can return 0).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(int_log)]
|
||||
#[doc = concat!("assert_eq!(5", stringify!($SelfT), ".log(5), 1);")]
|
||||
/// ```
|
||||
#[unstable(feature = "int_log", issue = "70887")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
#[rustc_inherit_overflow_checks]
|
||||
#[allow(arithmetic_overflow)]
|
||||
pub const fn log(self, base: Self) -> Self {
|
||||
match self.checked_log(base) {
|
||||
Some(n) => n,
|
||||
None => {
|
||||
// In debug builds, trigger a panic on None.
|
||||
// This should optimize completely out in release builds.
|
||||
let _ = Self::MAX + 1;
|
||||
|
||||
0
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the base 2 logarithm of the number.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// When the number is negative or zero it panics in debug mode and
|
||||
/// the return value is wrapped to 0 in release mode (the only situation in
|
||||
/// which the method can return 0).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(int_log)]
|
||||
#[doc = concat!("assert_eq!(2", stringify!($SelfT), ".log2(), 1);")]
|
||||
/// ```
|
||||
#[unstable(feature = "int_log", issue = "70887")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
#[rustc_inherit_overflow_checks]
|
||||
#[allow(arithmetic_overflow)]
|
||||
pub const fn log2(self) -> Self {
|
||||
match self.checked_log2() {
|
||||
Some(n) => n,
|
||||
None => {
|
||||
// In debug builds, trigger a panic on None.
|
||||
// This should optimize completely out in release builds.
|
||||
let _ = Self::MAX + 1;
|
||||
|
||||
0
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the base 10 logarithm of the number.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// When the number is negative or zero it panics in debug mode and the
|
||||
/// return value is wrapped to 0 in release mode (the only situation in
|
||||
/// which the method can return 0).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(int_log)]
|
||||
#[doc = concat!("assert_eq!(10", stringify!($SelfT), ".log10(), 1);")]
|
||||
/// ```
|
||||
#[unstable(feature = "int_log", issue = "70887")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
#[rustc_inherit_overflow_checks]
|
||||
#[allow(arithmetic_overflow)]
|
||||
pub const fn log10(self) -> Self {
|
||||
match self.checked_log10() {
|
||||
Some(n) => n,
|
||||
None => {
|
||||
// In debug builds, trigger a panic on None.
|
||||
// This should optimize completely out in release builds.
|
||||
let _ = Self::MAX + 1;
|
||||
|
||||
0
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// 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;
|
||||
/// `checked_log2` can produce results more efficiently for base 2, and
|
||||
/// `checked_log10` can produce results more efficiently for base 10.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(int_log)]
|
||||
#[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_log(5), Some(1));")]
|
||||
/// ```
|
||||
#[unstable(feature = "int_log", issue = "70887")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline]
|
||||
pub const fn checked_log(self, base: Self) -> Option<Self> {
|
||||
if self <= 0 || base <= 1 {
|
||||
None
|
||||
} else {
|
||||
let mut n = 0;
|
||||
let mut r = self;
|
||||
|
||||
// Optimization for 128 bit wide integers.
|
||||
if Self::BITS == 128 {
|
||||
let b = Self::log2(self) / (Self::log2(base) + 1);
|
||||
n += b;
|
||||
r /= base.pow(b as u32);
|
||||
}
|
||||
|
||||
while r >= base {
|
||||
r /= base;
|
||||
n += 1;
|
||||
}
|
||||
Some(n)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the base 2 logarithm of the number.
|
||||
///
|
||||
/// Returns `None` if the number is zero.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(int_log)]
|
||||
#[doc = concat!("assert_eq!(2", stringify!($SelfT), ".checked_log2(), Some(1));")]
|
||||
/// ```
|
||||
#[unstable(feature = "int_log", issue = "70887")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline]
|
||||
pub const fn checked_log2(self) -> Option<Self> {
|
||||
if self <= 0 {
|
||||
None
|
||||
} else {
|
||||
// SAFETY: We just checked that this number is positive
|
||||
let log = (Self::BITS - 1) as Self - unsafe { intrinsics::ctlz_nonzero(self) };
|
||||
Some(log)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the base 10 logarithm of the number.
|
||||
///
|
||||
/// Returns `None` if the number is zero.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(int_log)]
|
||||
#[doc = concat!("assert_eq!(10", stringify!($SelfT), ".checked_log10(), Some(1));")]
|
||||
/// ```
|
||||
#[unstable(feature = "int_log", issue = "70887")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline]
|
||||
pub const fn checked_log10(self) -> Option<Self> {
|
||||
self.checked_log(10)
|
||||
}
|
||||
|
||||
/// Checked negation. Computes `-self`, returning `None` unless `self ==
|
||||
/// 0`.
|
||||
///
|
||||
|
@ -45,6 +45,7 @@
|
||||
#![feature(try_trait_v2)]
|
||||
#![feature(slice_internals)]
|
||||
#![feature(slice_partition_dedup)]
|
||||
#![feature(int_log)]
|
||||
#![feature(iter_advance_by)]
|
||||
#![feature(iter_partition_in_place)]
|
||||
#![feature(iter_intersperse)]
|
||||
|
99
library/core/tests/num/int_log.rs
Normal file
99
library/core/tests/num/int_log.rs
Normal file
@ -0,0 +1,99 @@
|
||||
//! This tests the `Integer::{log,log2,log10}` methods. These tests are in a
|
||||
//! separate file because there's both a large number of them, and not all tests
|
||||
//! can be run on Android. This is because in Android `log2` uses an imprecise
|
||||
//! approximation:https://github.com/rust-lang/rust/blob/4825e12fc9c79954aa0fe18f5521efa6c19c7539/src/libstd/sys/unix/android.rs#L27-L53
|
||||
|
||||
#[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));
|
||||
|
||||
assert_eq!(0u8.checked_log(4), None);
|
||||
assert_eq!(0u16.checked_log(4), None);
|
||||
assert_eq!(0i8.checked_log(4), None);
|
||||
assert_eq!(0i16.checked_log(4), None);
|
||||
|
||||
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);
|
||||
|
||||
assert_eq!(0u8.checked_log2(), None);
|
||||
assert_eq!(0u16.checked_log2(), None);
|
||||
assert_eq!(0i8.checked_log2(), None);
|
||||
assert_eq!(0i16.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 {
|
||||
// Guard against Android's imprecise f32::log2 implementation.
|
||||
if i != 8192 && i != 32768 {
|
||||
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 {
|
||||
// Guard against Android's imprecise f32::log2 implementation.
|
||||
if i != 8192 {
|
||||
assert_eq!(i.checked_log2(), Some((i as f32).log2() as i16));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate cases that fail on Android's imprecise float log2 implementation.
|
||||
#[test]
|
||||
#[cfg(not(target_os = "android"))]
|
||||
fn checked_log2_not_android() {
|
||||
assert_eq!(8192u16.checked_log2(), Some((8192f32).log2() as u16));
|
||||
assert_eq!(32768u16.checked_log2(), Some((32768f32).log2() as u16));
|
||||
assert_eq!(8192i16.checked_log2(), Some((8192f32).log2() as i16));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checked_log10() {
|
||||
assert_eq!(0u8.checked_log10(), None);
|
||||
assert_eq!(0u16.checked_log10(), None);
|
||||
assert_eq!(0i8.checked_log10(), None);
|
||||
assert_eq!(0i16.checked_log10(), None);
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
@ -29,6 +29,7 @@
|
||||
mod bignum;
|
||||
mod dec2flt;
|
||||
mod flt2dec;
|
||||
mod int_log;
|
||||
mod ops;
|
||||
mod wrapping;
|
||||
|
||||
|
@ -279,6 +279,7 @@
|
||||
#![feature(hashmap_internals)]
|
||||
#![feature(int_error_internals)]
|
||||
#![feature(integer_atomics)]
|
||||
#![feature(int_log)]
|
||||
#![feature(into_future)]
|
||||
#![feature(intra_doc_pointers)]
|
||||
#![feature(iter_zip)]
|
||||
|
Loading…
Reference in New Issue
Block a user