Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Integer::log variants #80918

Merged
merged 1 commit into from
Jul 7, 2021
Merged

Add Integer::log variants #80918

merged 1 commit into from
Jul 7, 2021

Conversation

yoshuawuyts
Copy link
Member

This is another attempt at landing #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

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 to arrive at base10. However because we're performing intermediate rounding we arrive at the wrong result:

// 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

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

@rust-highfive
Copy link
Collaborator

r? @m-ou-se

(rust-highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jan 11, 2021
@yoshuawuyts
Copy link
Member Author

yoshuawuyts commented Jan 11, 2021

This patch still needs to implement the workaround mentioned by @tspiteri in #70835 (comment):

Maybe a workaround would be to skip 8192 and 32768 in the 1..=u16::MAX loop and 8192 in the 1..=i16::MAX loop, and add explicit checks for those three cases outside the loops without using f32::log2 (together with a comment that f32::log2 is not used in those cases because of android).

The tests are expected to pass CI, but fail once merged and Bors runs.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@yoshuawuyts
Copy link
Member Author

Okay, applied the fix from mentioned above. Fingers crossed this passes now.

@rust-log-analyzer

This comment has been minimized.

@yoshuawuyts
Copy link
Member Author

Addressed the feedback from @nagisa and @the8472

@CDirkx
Copy link
Contributor

CDirkx commented Jan 14, 2021

All of these methods could also be made const; that would only depend on const_option for Option::unwrap, which I think is okay?

@bors
Copy link
Contributor

bors commented Jan 14, 2021

☔ The latest upstream changes (presumably #78259) made this pull request unmergeable. Please resolve the merge conflicts.

@yoshuawuyts
Copy link
Member Author

Fixed the merge conflict and applied @CDirkx's feedback: all methods are now const.

@yoshuawuyts yoshuawuyts changed the title Add Integer::{log,log2,log10} variants Add Integer::log variants Jan 15, 2021
@rust-log-analyzer

This comment has been minimized.

@EdorianDark
Copy link
Contributor

EdorianDark commented Jan 16, 2021

I am against the names, since they are very confusing.

Lets look againg at what other languages do for log on an integer:

C++: integer is cast to double and then log is called
C: integer is cast to double and then log is called
Python: uses the C functions
Javascript: had only floats for a long time. The recently introduced BigInt does not have log.
Java: int is implicitly converted to double and then log called
C#: int is implicitly converted to double and then log called

Is there any example to call the integer logarithm log?
I think the naming issue should be resolved before merging this.

@the8472
Copy link
Member

the8472 commented Jan 16, 2021

I think the naming issue should be resolved before merging this.

The methods are unstable, so this could also be discussed before stabilization.

@yoshuawuyts
Copy link
Member Author

@EdorianDark you've expressed that position repeatedly in earlier threads; namely: #70887 (comment) and #70835 (comment). We concluded in #70835 (comment) that naming should not be a blocker for landing this PR (I commented, you +1'd it).

I don't believe much has changed in the interim, and still hold the position that we don't need to finalize the names in order to move forward with adding this functionality to nightly.

@EdorianDark
Copy link
Contributor

@yoshuawuyts i changed my mind.
The only thing what will change after this is merged, is that code will be written using these functions.
This would it make renaming it even harder.

I feel that in the tracking issue wasn't enough discussion about the names.

Of cause my opinion isn't that important since I am not in any Rust team.

@m-ou-se
Copy link
Member

m-ou-se commented Jan 24, 2021

This would produce a very generic panic message in the non-checked_ versions. None of the other methods on integers produce a panic message like that. All of them originate in a panic from one of the primitive operations (/, +, .. etc.).

Except for division by zero (which would be undefined behaviour), all other panics on the integer methods and operators only happen in debug mode. In release mode, they wrap around or return zero. For example, next_power_of_two's documentation states:

        /// When return value overflows (i.e., `self > (1 << (N-1))` for type
        /// `uN`), it panics in debug mode and return value is wrapped to 0 in
        /// release mode (the only situation in which method can return 0).

Should we keep that consistency?

library/core/src/num/int_macros.rs Outdated Show resolved Hide resolved
library/core/src/num/int_macros.rs Outdated Show resolved Hide resolved
@m-ou-se m-ou-se added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jan 24, 2021
@bors
Copy link
Contributor

bors commented Jan 31, 2021

☔ The latest upstream changes (presumably #81578) made this pull request unmergeable. Please resolve the merge conflicts.

@yoshuawuyts
Copy link
Member Author

Should we keep that consistency?

I hadn't considered this, and I think we absolutely should.


For now I've updated the PR with the other feedback and fixed the merge conflict. But I believe what we should then do is:

  • In debug mode .expect with an error which explains what's going on.
  • In non-debug .or(0) the numbers so in case of None we round to zero.

Is that accurate?

@yoshuawuyts
Copy link
Member Author

cc/ @m-ou-se this should be ready for another review when you have time

@the8472 the8472 added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Jun 25, 2021
@yoshuawuyts
Copy link
Member Author

Was halfway through making dinner when I realized that we could just turn off the overflow errors here, evading the use of Add::add and re-enabling these functions as const fn. In fact it seems something similar has been done before in f238148. Glad I managed to tick that extra box! :D

Copy link
Member

@m-ou-se m-ou-se left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me! (After #80918 (comment) is fixed.)

Can you update the tracking issue to use our new template? That includes a short overview of the public API.

@m-ou-se
Copy link
Member

m-ou-se commented Jul 6, 2021

@bors r+

Thanks for making this happen, @yoshuawuyts!

@m-ou-se
Copy link
Member

m-ou-se commented Jul 6, 2021

@bors ping?

@m-ou-se m-ou-se closed this Jul 6, 2021
@m-ou-se m-ou-se reopened this Jul 6, 2021
@m-ou-se
Copy link
Member

m-ou-se commented Jul 6, 2021

@bors r+

@bors
Copy link
Contributor

bors commented Jul 6, 2021

📌 Commit 9f57996 has been approved by m-ou-se

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 6, 2021
bors added a commit to rust-lang-ci/rust that referenced this pull request Jul 7, 2021
Rollup of 8 pull requests

Successful merges:

 - rust-lang#80918 (Add Integer::log variants)
 - rust-lang#86717 (Rename some Rust 2021 lints to better names )
 - rust-lang#86819 (Clean up rustdoc IDs)
 - rust-lang#86880 (Test ManuallyDrop::clone_from.)
 - rust-lang#86906 (Replace deprecated compare_and_swap and fix typo in core::sync::atomic::{fence, compiler_fence} docs)
 - rust-lang#86907 (Migrate `cpu-usage-over-time.py` to Python 3)
 - rust-lang#86916 (rewrote documentation for thread::yield_now())
 - rust-lang#86919 (Update books)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
@bors
Copy link
Contributor

bors commented Jul 7, 2021

⌛ Testing commit 9f57996 with merge c5e344f...

@bors bors merged commit 9bbc470 into rust-lang:master Jul 7, 2021
@rustbot rustbot added this to the 1.55.0 milestone Jul 7, 2021
@yoshuawuyts yoshuawuyts deleted the int-log2 branch July 7, 2021 11:17
bors added a commit to rust-lang-ci/rust that referenced this pull request Jul 8, 2021
special case for integer log10

Now that rust-lang#80918 has been merged, this PR provides a faster version of `log10`.

The PR also adds some tests for values close to all powers of 10.
flip1995 pushed a commit to flip1995/rust that referenced this pull request Jul 15, 2021
Rollup of 8 pull requests

Successful merges:

 - rust-lang#80918 (Add Integer::log variants)
 - rust-lang#86717 (Rename some Rust 2021 lints to better names )
 - rust-lang#86819 (Clean up rustdoc IDs)
 - rust-lang#86880 (Test ManuallyDrop::clone_from.)
 - rust-lang#86906 (Replace deprecated compare_and_swap and fix typo in core::sync::atomic::{fence, compiler_fence} docs)
 - rust-lang#86907 (Migrate `cpu-usage-over-time.py` to Python 3)
 - rust-lang#86916 (rewrote documentation for thread::yield_now())
 - rust-lang#86919 (Update books)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
@mstankus
Copy link

Question: I am new to rust and am a mathematician.
log base 2 of 0 is either not defined or NaN.
log base 2 of 1 through 255 is between 0 and 7.
Why are the log2, log10 and log functions return u32?

Thanks.

@EdorianDark
Copy link
Contributor

EdorianDark commented Jul 17, 2022

Question: I am new to rust and am a mathematician. log base 2 of 0 is either not defined or NaN. log base 2 of 1 through 255 is between 0 and 7. Why are the log2, log10 and log functions return u32?

Thanks.

This merge has been closed for a while. A better place to discuss things would be at the tracking issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.