rust/library/core/tests/num
Yuki Okushi 9bbc470e97
Rollup merge of #80918 - yoshuawuyts:int-log2, r=m-ou-se
Add Integer::log variants

_This is another attempt at landing https://github.com/rust-lang/rust/pull/70835, which was approved by the libs team but failed on Android tests through Bors. The text copied here is from the original issue. The only change made so far is the addition of non-`checked_` variants of the log methods._

_Tracking issue: #70887_

---

This implements `{log,log2,log10}` methods for all integer types. The implementation was provided by `@substack` for use in the stdlib.

_Note: I'm not big on math, so this PR is a best effort written with limited knowledge. It's likely I'll be getting things wrong, but happy to learn and correct. Please bare with me._

## Motivation
Calculating the logarithm of a number is a generally useful operation. Currently the stdlib only provides implementations for floats, which means that if we want to calculate the logarithm for an integer we have to cast it to a float and then back to an int.

> would be nice if there was an integer log2 instead of having to either use the f32 version or leading_zeros() which i have to verify the results of every time to be sure

_— [`@substack,` 2020-03-08](https://twitter.com/substack/status/1236445105197727744)_

At higher numbers converting from an integer to a float we also risk overflows. This means that Rust currently only provides log operations for a limited set of integers.

The process of doing log operations by converting between floats and integers is also prone to rounding errors. In the following example we're trying to calculate `base10` for an integer. We might try and calculate the `base2` for the values, and attempt [a base swap](https://www.rapidtables.com/math/algebra/Logarithm.html#log-rules) to arrive at `base10`. However because we're performing intermediate rounding we arrive at the wrong result:

```rust
// log10(900) = ~2.95 = 2
dbg!(900f32.log10() as u64);

// log base change rule: logb(x) = logc(x) / logc(b)
// log2(900) / log2(10) = 9/3 = 3
dbg!((900f32.log2() as u64) / (10f32.log2() as u64));
```
_[playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=6bd6c68b3539e400f9ca4fdc6fc2eed0)_

This is somewhat nuanced as a lot of the time it'll work well, but in real world code this could lead to some hard to track bugs. By providing correct log implementations directly on integers we can help prevent errors around this.

## Implementation notes

I checked whether LLVM intrinsics existed before implementing this, and none exist yet. ~~Also I couldn't really find a better way to write the `ilog` function. One option would be to make it a private method on the number, but I didn't see any precedent for that. I also didn't know where to best place the tests, so I added them to the bottom of the file. Even though they might seem like quite a lot they take no time to execute.~~

## References

- [Log rules](https://www.rapidtables.com/math/algebra/Logarithm.html#log-rules)
- [Rounding error playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=6bd6c68b3539e400f9ca4fdc6fc2eed0)
- [substack's tweet asking about integer log2 in the stdlib](https://twitter.com/substack/status/1236445105197727744)
- [Integer Logarithm, A. Jaffer 2008](https://people.csail.mit.edu/jaffer/III/ilog.pdf)
2021-07-07 12:17:32 +09:00
..
dec2flt Re-enable all num tests on WASM 2021-01-15 16:58:44 -05:00
flt2dec Move flt2dec::{Formatted, Part} to dedicated module 2021-06-06 02:54:51 +01:00
bignum.rs
i8.rs
i16.rs
i32.rs Minor refactoring 2020-09-05 17:07:53 +05:30
i64.rs
i128.rs Add u128 and i128 integer tests 2020-11-14 20:27:08 +01:00
ieee754.rs Add IEEE754 tests 2021-03-22 17:02:06 -07:00
int_log.rs Add Integer::{log,log2,log10} variants 2021-06-25 18:52:46 +02:00
int_macros.rs Add u128 and i128 integer tests 2020-11-14 20:27:08 +01:00
mod.rs Add Integer::{log,log2,log10} variants 2021-06-25 18:52:46 +02:00
nan.rs Update tests to remove old numeric constants 2020-11-29 00:55:55 -05:00
ops.rs Adds tests to ensure some base op traits exist. 2021-01-13 23:14:00 -05:00
u8.rs
u16.rs
u32.rs
u64.rs
u128.rs Add u128 and i128 integer tests 2020-11-14 20:27:08 +01:00
uint_macros.rs Add u128 and i128 integer tests 2020-11-14 20:27:08 +01:00
wrapping.rs Avoid ident concatenation in macro. 2021-01-13 23:13:55 -05:00