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::checked_{log,log2,log10} #70835

Closed
wants to merge 2 commits into from
Closed

Add Integer::checked_{log,log2,log10} #70835

wants to merge 2 commits into from

Conversation

yoshuawuyts
Copy link
Member

@yoshuawuyts yoshuawuyts commented Apr 6, 2020

edit: this PR has moved to #80918

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? @cramertj

(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 Apr 6, 2020
@rust-highfive
Copy link
Collaborator

The job x86_64-gnu-llvm-7 of your PR failed (pretty log, raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem.

Click to expand the log.
2020-04-06T11:18:16.0124966Z ========================== Starting Command Output ===========================
2020-04-06T11:18:16.0127246Z [command]/bin/bash --noprofile --norc /home/vsts/work/_temp/0d90890c-9ace-4003-9748-0c2f77e0b4b3.sh
2020-04-06T11:18:16.0127447Z 
2020-04-06T11:18:16.0131266Z ##[section]Finishing: Disable git automatic line ending conversion
2020-04-06T11:18:16.0149143Z ##[section]Starting: Checkout rust-lang/rust@refs/pull/70835/merge to s
2020-04-06T11:18:16.0152905Z Task         : Get sources
2020-04-06T11:18:16.0153143Z Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
2020-04-06T11:18:16.0153370Z Version      : 1.0.0
2020-04-06T11:18:16.0153529Z Author       : Microsoft
---
2020-04-06T11:18:17.0129778Z ##[command]git remote add origin https://github.com/rust-lang/rust
2020-04-06T11:18:17.0133697Z ##[command]git config gc.auto 0
2020-04-06T11:18:17.0136352Z ##[command]git config --get-all http.https://github.com/rust-lang/rust.extraheader
2020-04-06T11:18:17.0138789Z ##[command]git config --get-all http.proxy
2020-04-06T11:18:17.0143343Z ##[command]git -c http.extraheader="AUTHORIZATION: basic ***" fetch --force --tags --prune --progress --no-recurse-submodules --depth=2 origin +refs/heads/*:refs/remotes/origin/* +refs/pull/70835/merge:refs/remotes/pull/70835/merge
---
2020-04-06T11:20:20.6855766Z Looks like docker image is the same as before, not uploading
2020-04-06T11:20:28.3254661Z [CI_JOB_NAME=x86_64-gnu-llvm-7]
2020-04-06T11:20:28.3533903Z [CI_JOB_NAME=x86_64-gnu-llvm-7]
2020-04-06T11:20:28.3554858Z == clock drift check ==
2020-04-06T11:20:28.3574188Z   local time: Mon Apr  6 11:20:28 UTC 2020
2020-04-06T11:20:28.5174028Z   network time: Mon, 06 Apr 2020 11:20:28 GMT
2020-04-06T11:20:28.5196659Z Starting sccache server...
2020-04-06T11:20:28.5909597Z configure: processing command line
2020-04-06T11:20:28.5910188Z configure: 
2020-04-06T11:20:28.5911394Z configure: rust.dist-src        := False
---
2020-04-06T11:24:57.2849562Z    Compiling rustc_feature v0.0.0 (/checkout/src/librustc_feature)
2020-04-06T11:24:58.6292070Z    Compiling fmt_macros v0.0.0 (/checkout/src/libfmt_macros)
2020-04-06T11:25:00.0188182Z    Compiling rustc_ast_pretty v0.0.0 (/checkout/src/librustc_ast_pretty)
2020-04-06T11:25:01.6466592Z    Compiling rustc_hir v0.0.0 (/checkout/src/librustc_hir)
2020-04-06T11:25:08.7408448Z    Compiling rustc_query_system v0.0.0 (/checkout/src/librustc_query_system)
2020-04-06T11:25:11.6173960Z    Compiling rustc_hir_pretty v0.0.0 (/checkout/src/librustc_hir_pretty)
2020-04-06T11:25:15.5661315Z    Compiling rustc_attr v0.0.0 (/checkout/src/librustc_attr)
2020-04-06T11:25:19.1721665Z    Compiling rustc_parse v0.0.0 (/checkout/src/librustc_parse)
2020-04-06T11:25:26.8527216Z    Compiling rustc_ast_lowering v0.0.0 (/checkout/src/librustc_ast_lowering)
---
2020-04-06T11:45:17.2210771Z    Compiling rustc_feature v0.0.0 (/checkout/src/librustc_feature)
2020-04-06T11:45:18.7937811Z    Compiling fmt_macros v0.0.0 (/checkout/src/libfmt_macros)
2020-04-06T11:45:20.5316799Z    Compiling rustc_ast_pretty v0.0.0 (/checkout/src/librustc_ast_pretty)
2020-04-06T11:45:22.0206274Z    Compiling rustc_hir v0.0.0 (/checkout/src/librustc_hir)
2020-04-06T11:45:31.4368992Z    Compiling rustc_query_system v0.0.0 (/checkout/src/librustc_query_system)
2020-04-06T11:45:34.1367268Z    Compiling rustc_hir_pretty v0.0.0 (/checkout/src/librustc_hir_pretty)
2020-04-06T11:45:38.8053470Z    Compiling rustc_attr v0.0.0 (/checkout/src/librustc_attr)
2020-04-06T11:45:43.6956772Z    Compiling rustc_parse v0.0.0 (/checkout/src/librustc_parse)
2020-04-06T11:45:53.3635560Z    Compiling rustc_ast_lowering v0.0.0 (/checkout/src/librustc_ast_lowering)
---
2020-04-06T12:08:03.8053902Z .................................................................................................... 1700/9878
2020-04-06T12:08:07.2938155Z .................................................................................................... 1800/9878
2020-04-06T12:08:15.0441742Z .................................................................................................i.. 1900/9878
2020-04-06T12:08:21.8874337Z .................................................................................................... 2000/9878
2020-04-06T12:08:27.4173617Z .......................................................................................iiiii........ 2100/9878
2020-04-06T12:08:45.5873483Z .................................................................................................... 2300/9878
2020-04-06T12:08:47.3563929Z .................................................................................................... 2400/9878
2020-04-06T12:08:49.2159640Z .................................................................................................... 2500/9878
2020-04-06T12:08:54.3482036Z .................................................................................................... 2600/9878
---
2020-04-06T12:11:21.6149790Z .............................................................i...............i...................... 5000/9878
2020-04-06T12:11:27.7001922Z .................................................................................................... 5100/9878
2020-04-06T12:11:34.0453609Z .................................................................................................... 5200/9878
2020-04-06T12:11:38.4087519Z ......i............................................................................................. 5300/9878
2020-04-06T12:11:47.2977202Z ...............................................................................................ii.ii 5400/9878
2020-04-06T12:11:52.5435938Z ........i...i....................................................................................... 5500/9878
2020-04-06T12:12:01.2335371Z ........................................i........................................................... 5700/9878
2020-04-06T12:12:01.2335371Z ........................................i........................................................... 5700/9878
2020-04-06T12:12:10.1337194Z ............................................................ii.....................................i 5800/9878
2020-04-06T12:12:21.9410803Z .................................................................................................... 6000/9878
2020-04-06T12:12:21.9410803Z .................................................................................................... 6000/9878
2020-04-06T12:12:30.6071890Z .............................................................................................ii...i. 6100/9878
2020-04-06T12:12:41.0570314Z .ii...........i..................................................................................... 6200/9878
2020-04-06T12:12:54.4274875Z .................................................................................................... 6400/9878
2020-04-06T12:12:58.9601803Z .................................................................................................... 6500/9878
2020-04-06T12:12:58.9601803Z .................................................................................................... 6500/9878
2020-04-06T12:13:21.1913382Z .......................i..ii........................................................................ 6600/9878
2020-04-06T12:13:39.9455843Z .................................................................................................... 6800/9878
2020-04-06T12:13:41.7753857Z .......................i............................................................................ 6900/9878
2020-04-06T12:13:43.4809766Z .................................................................................................... 7000/9878
2020-04-06T12:13:45.3319170Z ..............................................................i..................................... 7100/9878
---
2020-04-06T12:15:13.4808128Z .................................................................................................... 7800/9878
2020-04-06T12:15:17.0816012Z .................................................................................................... 7900/9878
2020-04-06T12:15:22.0886198Z .................................................................................................... 8000/9878
2020-04-06T12:15:28.8167168Z ...........................i........................................................................ 8100/9878
2020-04-06T12:15:35.6895560Z ............................................................................iiiiiiiiii.i............ 8200/9878
2020-04-06T12:15:49.1331297Z ....................i......i........................................................................ 8400/9878
2020-04-06T12:15:53.1114994Z .................................................................................................... 8500/9878
2020-04-06T12:16:02.3090505Z .................................................................................................... 8600/9878
2020-04-06T12:16:12.7389594Z .................................................................................................... 8700/9878
---
2020-04-06T12:18:15.5989733Z Suite("src/test/codegen") not skipped for "bootstrap::test::Codegen" -- not in ["src/tools/tidy"]
2020-04-06T12:18:15.6145284Z Check compiletest suite=codegen mode=codegen (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
2020-04-06T12:18:15.7847104Z 
2020-04-06T12:18:15.7847491Z running 185 tests
2020-04-06T12:18:17.9573690Z iiii......i............ii.i..iiii....i....i...........i............i..i..................i....i..... 100/185
2020-04-06T12:18:20.1231056Z .......i.i.i...iii..iiiiiiiiiiiiiiii.......................iii...............ii......
2020-04-06T12:18:20.1234265Z 
2020-04-06T12:18:20.1239880Z  finished in 4.509
2020-04-06T12:18:20.1245684Z Suite("src/test/codegen-units") not skipped for "bootstrap::test::CodegenUnits" -- not in ["src/tools/tidy"]
2020-04-06T12:18:20.1402065Z Check compiletest suite=codegen-units mode=codegen-units (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
---
2020-04-06T12:18:21.8950138Z Suite("src/test/assembly") not skipped for "bootstrap::test::Assembly" -- not in ["src/tools/tidy"]
2020-04-06T12:18:21.9097649Z Check compiletest suite=assembly mode=assembly (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
2020-04-06T12:18:22.0344715Z 
2020-04-06T12:18:22.0345331Z running 9 tests
2020-04-06T12:18:22.0346241Z iiiiiiiii
2020-04-06T12:18:22.0348754Z 
2020-04-06T12:18:22.0349588Z  finished in 0.125
2020-04-06T12:18:22.0351428Z Suite("src/test/incremental") not skipped for "bootstrap::test::Incremental" -- not in ["src/tools/tidy"]
2020-04-06T12:18:22.0485661Z Check compiletest suite=incremental mode=incremental (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
---
2020-04-06T12:18:38.8558620Z Suite("src/test/debuginfo") not skipped for "bootstrap::test::Debuginfo" -- not in ["src/tools/tidy"]
2020-04-06T12:18:38.8741455Z Check compiletest suite=debuginfo mode=debuginfo (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
2020-04-06T12:18:39.0274418Z 
2020-04-06T12:18:39.0275835Z running 115 tests
2020-04-06T12:18:50.5270367Z iiiii..i.....i..i...i..i.i.i..i..i..ii....i.i....ii..........iiii.........i.....i..i.......ii.i.ii.. 100/115
2020-04-06T12:18:51.9313106Z ...iiii.....ii.
2020-04-06T12:18:51.9315218Z 
2020-04-06T12:18:51.9316153Z  finished in 13.057
2020-04-06T12:18:51.9329740Z Suite("src/test/ui-fulldeps") not skipped for "bootstrap::test::UiFullDeps" -- not in ["src/tools/tidy"]
2020-04-06T12:18:51.9332395Z Uplifting stage1 rustc (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
---
2020-04-06T12:29:47.9376920Z 
2020-04-06T12:29:47.9377777Z    Doc-tests core
2020-04-06T12:29:51.9708456Z 
2020-04-06T12:29:51.9709989Z running 2508 tests
2020-04-06T12:29:59.8766142Z ......iiiii......................................................................................... 100/2508
2020-04-06T12:30:07.6360898Z .....................................................................................ii............. 200/2508
2020-04-06T12:30:26.2354422Z ....................i............................................................................... 400/2508
2020-04-06T12:30:26.2354422Z ....................i............................................................................... 400/2508
2020-04-06T12:30:34.7727547Z ..........................................................................i..i..................iiii 500/2508
2020-04-06T12:30:48.7950337Z ............................................................................FF.F.................... 700/2508
2020-04-06T12:30:55.8795421Z ......................................................FF.F.......................................... 800/2508
2020-04-06T12:31:02.8642474Z ................................FF.F................................................................ 900/2508
2020-04-06T12:31:02.8642474Z ................................FF.F................................................................ 900/2508
2020-04-06T12:31:09.7444393Z ..........FF.F..........................................................................FF.F........ 1000/2508
2020-04-06T12:31:16.7513721Z ..................................................................FF.F.............................. 1100/2508
2020-04-06T12:31:31.0650024Z .................................................................................................... 1300/2508
2020-04-06T12:31:38.0446313Z .................................................................................................... 1400/2508
2020-04-06T12:31:45.1656218Z .................................................................................................... 1500/2508
2020-04-06T12:31:52.9161530Z .................................................................................................... 1600/2508
---
2020-04-06T12:33:05.9920102Z 
2020-04-06T12:33:05.9920456Z error[E0658]: use of unstable library feature 'int_log'
2020-04-06T12:33:05.9920802Z  --> num/mod.rs:56:19
2020-04-06T12:33:05.9920938Z   |
2020-04-06T12:33:05.9921095Z 9 | let result = five.log(5);
2020-04-06T12:33:05.9921399Z   |
2020-04-06T12:33:05.9921399Z   |
2020-04-06T12:33:05.9921621Z   = help: add `#![feature(int_log)]` to the crate attributes to enable
2020-04-06T12:33:05.9921957Z error: aborting due to 2 previous errors
2020-04-06T12:33:05.9922097Z 
2020-04-06T12:33:05.9922464Z For more information about this error, try `rustc --explain E0658`.
2020-04-06T12:33:05.9922812Z Couldn't compile the test.
---
2020-04-06T12:33:05.9926674Z   |                  ^^^^^ method, not a field
2020-04-06T12:33:05.9926836Z   |
2020-04-06T12:33:05.9926999Z help: use parentheses to call the method
2020-04-06T12:33:05.9927173Z   |
2020-04-06T12:33:05.9927487Z 9 | let result = ten.log10();
2020-04-06T12:33:05.9927773Z 
2020-04-06T12:33:05.9928118Z error: aborting due to 2 previous errors
2020-04-06T12:33:05.9928257Z 
2020-04-06T12:33:05.9928613Z For more information about this error, try `rustc --explain E0615`.
---
2020-04-06T12:33:05.9938855Z 
2020-04-06T12:33:05.9939173Z error[E0658]: use of unstable library feature 'int_log'
2020-04-06T12:33:05.9939475Z  --> num/mod.rs:56:19
2020-04-06T12:33:05.9939597Z   |
2020-04-06T12:33:05.9939741Z 9 | let result = five.log(5);
2020-04-06T12:33:05.9940026Z   |
2020-04-06T12:33:05.9940026Z   |
2020-04-06T12:33:05.9940212Z   = help: add `#![feature(int_log)]` to the crate attributes to enable
2020-04-06T12:33:05.9940538Z error: aborting due to 2 previous errors
2020-04-06T12:33:05.9940664Z 
2020-04-06T12:33:05.9940991Z For more information about this error, try `rustc --explain E0658`.
2020-04-06T12:33:05.9941330Z Couldn't compile the test.
---
2020-04-06T12:33:05.9945760Z   |                  ^^^^^ method, not a field
2020-04-06T12:33:05.9945923Z   |
2020-04-06T12:33:05.9946079Z help: use parentheses to call the method
2020-04-06T12:33:05.9946235Z   |
2020-04-06T12:33:05.9946391Z 9 | let result = ten.log10();
2020-04-06T12:33:05.9946687Z 
2020-04-06T12:33:05.9946857Z error: aborting due to 2 previous errors
2020-04-06T12:33:05.9946996Z 
2020-04-06T12:33:05.9947526Z For more information about this error, try `rustc --explain E0615`.
---
2020-04-06T12:33:05.9956432Z 
2020-04-06T12:33:05.9956751Z error[E0658]: use of unstable library feature 'int_log'
2020-04-06T12:33:05.9957094Z  --> num/mod.rs:56:19
2020-04-06T12:33:05.9957225Z   |
2020-04-06T12:33:05.9957358Z 9 | let result = five.log(5);
2020-04-06T12:33:05.9957669Z   |
2020-04-06T12:33:05.9957669Z   |
2020-04-06T12:33:05.9957867Z   = help: add `#![feature(int_log)]` to the crate attributes to enable
2020-04-06T12:33:05.9958210Z error: aborting due to 2 previous errors
2020-04-06T12:33:05.9958344Z 
2020-04-06T12:33:05.9958681Z For more information about this error, try `rustc --explain E0658`.
2020-04-06T12:33:05.9959036Z Couldn't compile the test.
---
2020-04-06T12:33:05.9962740Z   |                  ^^^^^ method, not a field
2020-04-06T12:33:05.9962915Z   |
2020-04-06T12:33:05.9963067Z help: use parentheses to call the method
2020-04-06T12:33:05.9963283Z   |
2020-04-06T12:33:05.9963434Z 9 | let result = ten.log10();
2020-04-06T12:33:05.9963719Z 
2020-04-06T12:33:05.9963866Z error: aborting due to 2 previous errors
2020-04-06T12:33:05.9964018Z 
2020-04-06T12:33:05.9964428Z For more information about this error, try `rustc --explain E0615`.
---
2020-04-06T12:33:05.9974671Z 
2020-04-06T12:33:05.9975359Z error[E0658]: use of unstable library feature 'int_log'
2020-04-06T12:33:05.9975863Z  --> num/mod.rs:56:19
2020-04-06T12:33:05.9976034Z   |
2020-04-06T12:33:05.9976209Z 9 | let result = five.log(5);
2020-04-06T12:33:05.9976620Z   |
2020-04-06T12:33:05.9976620Z   |
2020-04-06T12:33:05.9976878Z   = help: add `#![feature(int_log)]` to the crate attributes to enable
2020-04-06T12:33:05.9977323Z error: aborting due to 2 previous errors
2020-04-06T12:33:05.9977834Z 
2020-04-06T12:33:05.9978429Z For more information about this error, try `rustc --explain E0658`.
2020-04-06T12:33:05.9978788Z Couldn't compile the test.
---
2020-04-06T12:33:05.9982887Z   |                  ^^^^^ method, not a field
2020-04-06T12:33:05.9983057Z   |
2020-04-06T12:33:05.9983201Z help: use parentheses to call the method
2020-04-06T12:33:05.9983343Z   |
2020-04-06T12:33:05.9983487Z 9 | let result = ten.log10();
2020-04-06T12:33:05.9983755Z 
2020-04-06T12:33:05.9983894Z error: aborting due to 2 previous errors
2020-04-06T12:33:06.0025520Z 
2020-04-06T12:33:06.0026092Z For more information about this error, try `rustc --explain E0615`.
---
2020-04-06T12:33:06.0034778Z 
2020-04-06T12:33:06.0035095Z error[E0658]: use of unstable library feature 'int_log'
2020-04-06T12:33:06.0035432Z  --> num/mod.rs:56:19
2020-04-06T12:33:06.0035565Z   |
2020-04-06T12:33:06.0035701Z 9 | let result = five.log(5);
2020-04-06T12:33:06.0036011Z   |
2020-04-06T12:33:06.0036011Z   |
2020-04-06T12:33:06.0036209Z   = help: add `#![feature(int_log)]` to the crate attributes to enable
2020-04-06T12:33:06.0036553Z error: aborting due to 2 previous errors
2020-04-06T12:33:06.0036688Z 
2020-04-06T12:33:06.0037027Z For more information about this error, try `rustc --explain E0658`.
2020-04-06T12:33:06.0037381Z Couldn't compile the test.
---
2020-04-06T12:33:06.0041383Z   |                  ^^^^^ method, not a field
2020-04-06T12:33:06.0041557Z   |
2020-04-06T12:33:06.0041709Z help: use parentheses to call the method
2020-04-06T12:33:06.0041866Z   |
2020-04-06T12:33:06.0042001Z 9 | let result = ten.log10();
2020-04-06T12:33:06.0042303Z 
2020-04-06T12:33:06.0042450Z error: aborting due to 2 previous errors
2020-04-06T12:33:06.0042585Z 
2020-04-06T12:33:06.0042946Z For more information about this error, try `rustc --explain E0615`.
---
2020-04-06T12:33:06.0055132Z 
2020-04-06T12:33:06.0055677Z error[E0658]: use of unstable library feature 'int_log'
2020-04-06T12:33:06.0056653Z  --> num/mod.rs:56:19
2020-04-06T12:33:06.0056994Z   |
2020-04-06T12:33:06.0057151Z 9 | let result = five.log(5);
2020-04-06T12:33:06.0057520Z   |
2020-04-06T12:33:06.0057520Z   |
2020-04-06T12:33:06.0057826Z   = help: add `#![feature(int_log)]` to the crate attributes to enable
2020-04-06T12:33:06.0058424Z error: aborting due to 2 previous errors
2020-04-06T12:33:06.0058750Z 
2020-04-06T12:33:06.0059561Z For more information about this error, try `rustc --explain E0658`.
2020-04-06T12:33:06.0060357Z Couldn't compile the test.
---
2020-04-06T12:33:06.0071151Z   |                  ^^^^^ method, not a field
2020-04-06T12:33:06.0071357Z   |
2020-04-06T12:33:06.0071663Z help: use parentheses to call the method
2020-04-06T12:33:06.0071861Z   |
2020-04-06T12:33:06.0072037Z 9 | let result = ten.log10();
2020-04-06T12:33:06.0072434Z 
2020-04-06T12:33:06.0072626Z error: aborting due to 2 previous errors
2020-04-06T12:33:06.0073120Z 
2020-04-06T12:33:06.0074308Z For more information about this error, try `rustc --explain E0615`.
---
2020-04-06T12:33:06.0150602Z 
2020-04-06T12:33:06.0155559Z failed to run: /checkout/obj/build/bootstrap/debug/bootstrap test --exclude src/tools/tidy
2020-04-06T12:33:06.0155872Z Build completed unsuccessfully in 1:11:18
2020-04-06T12:33:06.0201752Z == clock drift check ==
2020-04-06T12:33:06.0217643Z   local time: Mon Apr  6 12:33:06 UTC 2020
2020-04-06T12:33:06.1282824Z   network time: Mon, 06 Apr 2020 12:33:06 GMT
2020-04-06T12:33:06.4763762Z 
2020-04-06T12:33:06.4763762Z 
2020-04-06T12:33:06.4841491Z ##[error]Bash exited with code '1'.
2020-04-06T12:33:06.4853361Z ##[section]Finishing: Run build
2020-04-06T12:33:06.4890683Z ##[section]Starting: Checkout rust-lang/rust@refs/pull/70835/merge to s
2020-04-06T12:33:06.4894835Z Task         : Get sources
2020-04-06T12:33:06.4895116Z Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
2020-04-06T12:33:06.4895354Z Version      : 1.0.0
2020-04-06T12:33:06.4895521Z Author       : Microsoft
2020-04-06T12:33:06.4895521Z Author       : Microsoft
2020-04-06T12:33:06.4895806Z Help         : [More Information](https://go.microsoft.com/fwlink/?LinkId=798199)
2020-04-06T12:33:06.4896112Z ==============================================================================
2020-04-06T12:33:06.7926833Z Cleaning any cached credential from repository: rust-lang/rust (GitHub)
2020-04-06T12:33:06.7965244Z ##[section]Finishing: Checkout rust-lang/rust@refs/pull/70835/merge to s
2020-04-06T12:33:06.8044621Z Cleaning up task key
2020-04-06T12:33:06.8045670Z Start cleaning up orphan processes.
2020-04-06T12:33:06.8221081Z Terminate orphan process: pid (3457) (python)
2020-04-06T12:33:06.8374070Z ##[section]Finishing: Finalize Job

I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact @rust-lang/infra. (Feature Requests)

@tspiteri
Copy link
Contributor

tspiteri commented Apr 6, 2020

In the tweet thread, there's a reference to C++ adding support in C++11, but the C++ function takes an integer parameter and returns a floating-point result.

Also, every time I remember actually needing anything like a log of an integer, it was to get the required number of bits to represent a value, so it's not even satisfied directly by this proposal; my use case is easily solved using 8 * size - leading_zeros(n) without having to unwrap. So I don't see the common use case of either an integer flooring log2, or an integer log with base other than 2. (There could be a use in some specific domains, but I'm not sure it would be of general use.)

@yoshuawuyts
Copy link
Member Author

(...) my use case is easily solved using 8 * size - leading_zeros(n) without having to unwrap. So I don't see the use case of either an integer flooring log2, or an integer log with base other than 2.

@tspiteri I'm confused by your argument here. This patch includes clear motivation for adding this method. It links to a real-world use case of a database vendor needing this functionality in Rust and expressing getting this right being tedious. The fact that this is a different use case than yours is unfortunate but that doesn't mean this is useless.


(...) the proposal returns the floor (...)

That's an interesting statement. I was under the impression that integer operations always floor unless specified otherwise. For example integer division works the same way:

// 7/3 = ~2.33 = 2
assert_eq!(7u8 / 3u8, 2u8);

playground

The ecosystem contains extensions for e.g. integer::div_ceil which would round the value up. However these are not part of std, and if they were introduced they would likely use a _ceil suffix as well.

@tspiteri
Copy link
Contributor

tspiteri commented Apr 6, 2020

I'm confused by your argument here. This patch includes clear motivation for adding this method. It links to a real-world use case of a database vendor needing this functionality in Rust and expressing getting this right being tedious.

I saw the bit about motivation as it could be tricky to get it right if required, but it didn't include a use case. Now going back to the twitter thread I see that there actually is a use case, but I have to scroll up to see it after following the link, so I had missed it the first time round. Looking into it, I see there is 0usize.leading_zeros() - (i+1).leading_zeros() - 1, which with the proposal could be replaced by (i+1).log2().unwrap(). So that is a use case for log2, but not for a general base, so I still cannot think of a use case for non-base-2 log of an integer returning an integer.

(...) the proposal returns the floor (...)

That's an interesting statement. I was under the impression that integer operations always floor unless specified otherwise.

Yeah, I saw that bit of my comment was rather weird so I edited off before I saw your reply, sorry about that.

@rust-highfive
Copy link
Collaborator

The job x86_64-gnu-llvm-7 of your PR failed (pretty log, raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem.

Click to expand the log.
2020-04-06T12:48:19.9878423Z ========================== Starting Command Output ===========================
2020-04-06T12:48:19.9883171Z [command]/bin/bash --noprofile --norc /home/vsts/work/_temp/c2295744-17ce-4576-8470-ccb2d290a207.sh
2020-04-06T12:48:19.9883680Z 
2020-04-06T12:48:19.9888885Z ##[section]Finishing: Disable git automatic line ending conversion
2020-04-06T12:48:19.9908564Z ##[section]Starting: Checkout rust-lang/rust@refs/pull/70835/merge to s
2020-04-06T12:48:19.9912145Z Task         : Get sources
2020-04-06T12:48:19.9912490Z Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
2020-04-06T12:48:19.9912759Z Version      : 1.0.0
2020-04-06T12:48:19.9912945Z Author       : Microsoft
---
2020-04-06T12:48:20.9890006Z ##[command]git remote add origin https://github.com/rust-lang/rust
2020-04-06T12:48:20.9896140Z ##[command]git config gc.auto 0
2020-04-06T12:48:20.9899876Z ##[command]git config --get-all http.https://github.com/rust-lang/rust.extraheader
2020-04-06T12:48:20.9903288Z ##[command]git config --get-all http.proxy
2020-04-06T12:48:20.9909641Z ##[command]git -c http.extraheader="AUTHORIZATION: basic ***" fetch --force --tags --prune --progress --no-recurse-submodules --depth=2 origin +refs/heads/*:refs/remotes/origin/* +refs/pull/70835/merge:refs/remotes/pull/70835/merge
---
2020-04-06T12:51:25.2193785Z Looks like docker image is the same as before, not uploading
2020-04-06T12:51:32.9244678Z [CI_JOB_NAME=x86_64-gnu-llvm-7]
2020-04-06T12:51:32.9670652Z [CI_JOB_NAME=x86_64-gnu-llvm-7]
2020-04-06T12:51:32.9703680Z == clock drift check ==
2020-04-06T12:51:32.9715047Z   local time: Mon Apr  6 12:51:32 UTC 2020
2020-04-06T12:51:33.1340116Z   network time: Mon, 06 Apr 2020 12:51:33 GMT
2020-04-06T12:51:33.1381746Z Starting sccache server...
2020-04-06T12:51:33.2249851Z configure: processing command line
2020-04-06T12:51:33.2251025Z configure: 
2020-04-06T12:51:33.2252397Z configure: rust.dist-src        := False
---
2020-04-06T12:57:15.3627498Z    Compiling rustc_feature v0.0.0 (/checkout/src/librustc_feature)
2020-04-06T12:57:17.0765947Z    Compiling fmt_macros v0.0.0 (/checkout/src/libfmt_macros)
2020-04-06T12:57:18.8732165Z    Compiling rustc_ast_pretty v0.0.0 (/checkout/src/librustc_ast_pretty)
2020-04-06T12:57:20.6050324Z    Compiling rustc_hir v0.0.0 (/checkout/src/librustc_hir)
2020-04-06T12:57:30.2579137Z    Compiling rustc_query_system v0.0.0 (/checkout/src/librustc_query_system)
2020-04-06T12:57:33.2454768Z    Compiling rustc_hir_pretty v0.0.0 (/checkout/src/librustc_hir_pretty)
2020-04-06T12:57:38.2067181Z    Compiling rustc_attr v0.0.0 (/checkout/src/librustc_attr)
2020-04-06T12:57:42.9396199Z    Compiling rustc_parse v0.0.0 (/checkout/src/librustc_parse)
2020-04-06T12:57:53.0984067Z    Compiling rustc_ast_lowering v0.0.0 (/checkout/src/librustc_ast_lowering)
---
2020-04-06T13:22:53.8056869Z    Compiling rustc_feature v0.0.0 (/checkout/src/librustc_feature)
2020-04-06T13:22:55.8123917Z    Compiling fmt_macros v0.0.0 (/checkout/src/libfmt_macros)
2020-04-06T13:22:58.0360457Z    Compiling rustc_ast_pretty v0.0.0 (/checkout/src/librustc_ast_pretty)
2020-04-06T13:22:59.7734791Z    Compiling rustc_hir v0.0.0 (/checkout/src/librustc_hir)
2020-04-06T13:23:11.8642326Z    Compiling rustc_query_system v0.0.0 (/checkout/src/librustc_query_system)
2020-04-06T13:23:15.0422029Z    Compiling rustc_hir_pretty v0.0.0 (/checkout/src/librustc_hir_pretty)
2020-04-06T13:23:20.9424167Z    Compiling rustc_attr v0.0.0 (/checkout/src/librustc_attr)
2020-04-06T13:23:26.9363979Z    Compiling rustc_parse v0.0.0 (/checkout/src/librustc_parse)
2020-04-06T13:23:39.1173696Z    Compiling rustc_ast_lowering v0.0.0 (/checkout/src/librustc_ast_lowering)
---
2020-04-06T13:51:57.4142026Z .................................................................................................... 1700/9878
2020-04-06T13:52:01.9133428Z .................................................................................................... 1800/9878
2020-04-06T13:52:11.7143746Z .................................................................................................i.. 1900/9878
2020-04-06T13:52:20.4083285Z .................................................................................................... 2000/9878
2020-04-06T13:52:27.5275863Z .......................................................................................iiiii........ 2100/9878
2020-04-06T13:52:50.6567435Z .................................................................................................... 2300/9878
2020-04-06T13:52:53.0075421Z .................................................................................................... 2400/9878
2020-04-06T13:52:55.4261003Z .................................................................................................... 2500/9878
2020-04-06T13:53:01.8818206Z .................................................................................................... 2600/9878
---
2020-04-06T13:56:13.4035382Z .............................................................i...............i...................... 5000/9878
2020-04-06T13:56:21.2801435Z .................................................................................................... 5100/9878
2020-04-06T13:56:29.6893898Z .................................................................................................... 5200/9878
2020-04-06T13:56:35.6249082Z ......i............................................................................................. 5300/9878
2020-04-06T13:56:46.8592505Z ...............................................................................................ii.ii 5400/9878
2020-04-06T13:56:52.4407300Z ........i...i....................................................................................... 5500/9878
2020-04-06T13:57:02.3686294Z ........................................i........................................................... 5700/9878
2020-04-06T13:57:02.3686294Z ........................................i........................................................... 5700/9878
2020-04-06T13:57:13.0943544Z ............................................................ii.....................................i 5800/9878
2020-04-06T13:57:26.8541141Z .................................................................................................... 6000/9878
2020-04-06T13:57:26.8541141Z .................................................................................................... 6000/9878
2020-04-06T13:57:37.4085573Z .............................................................................................ii...i. 6100/9878
2020-04-06T13:57:50.3337017Z .ii...........i..................................................................................... 6200/9878
2020-04-06T13:58:07.6872022Z .................................................................................................... 6400/9878
2020-04-06T13:58:13.8161685Z .................................................................................................... 6500/9878
2020-04-06T13:58:13.8161685Z .................................................................................................... 6500/9878
2020-04-06T13:58:30.2318403Z .......................i..ii........................................................................ 6600/9878
2020-04-06T13:58:52.9914966Z .................................................................................................... 6800/9878
2020-04-06T13:58:55.2758435Z .......................i............................................................................ 6900/9878
2020-04-06T13:58:57.5258890Z .................................................................................................... 7000/9878
2020-04-06T13:58:59.9594016Z ..............................................................i..................................... 7100/9878
---
2020-04-06T14:00:49.2856975Z .................................................................................................... 7800/9878
2020-04-06T14:00:54.3157280Z .................................................................................................... 7900/9878
2020-04-06T14:01:00.9974743Z .................................................................................................... 8000/9878
2020-04-06T14:01:09.8008867Z ...........................i........................................................................ 8100/9878
2020-04-06T14:01:19.1243819Z ............................................................................iiiiiiiiii.i............ 8200/9878
2020-04-06T14:01:36.9356347Z ....................i......i........................................................................ 8400/9878
2020-04-06T14:01:42.2761906Z .................................................................................................... 8500/9878
2020-04-06T14:01:54.2910090Z .................................................................................................... 8600/9878
2020-04-06T14:02:08.0539733Z .................................................................................................... 8700/9878
---
2020-04-06T14:04:49.6247010Z Suite("src/test/codegen") not skipped for "bootstrap::test::Codegen" -- not in ["src/tools/tidy"]
2020-04-06T14:04:49.6442832Z Check compiletest suite=codegen mode=codegen (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
2020-04-06T14:04:49.8664838Z 
2020-04-06T14:04:49.8665068Z running 185 tests
2020-04-06T14:04:52.9555770Z iiii......i............ii.i..iiii....i....i...........i............i..i..................i....i..... 100/185
2020-04-06T14:04:55.8718015Z .......i.i.i...iii..iiiiiiiiiiiiiiii.......................iii...............ii......
2020-04-06T14:04:55.8725030Z 
2020-04-06T14:04:55.8730957Z  finished in 6.228
2020-04-06T14:04:55.8738557Z Suite("src/test/codegen-units") not skipped for "bootstrap::test::CodegenUnits" -- not in ["src/tools/tidy"]
2020-04-06T14:04:55.8940771Z Check compiletest suite=codegen-units mode=codegen-units (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
---
2020-04-06T14:04:58.2827415Z Suite("src/test/assembly") not skipped for "bootstrap::test::Assembly" -- not in ["src/tools/tidy"]
2020-04-06T14:04:58.3068482Z Check compiletest suite=assembly mode=assembly (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
2020-04-06T14:04:58.4875065Z 
2020-04-06T14:04:58.4875450Z running 9 tests
2020-04-06T14:04:58.4876677Z iiiiiiiii
2020-04-06T14:04:58.4877706Z 
2020-04-06T14:04:58.4880590Z  finished in 0.181
2020-04-06T14:04:58.4891126Z Suite("src/test/incremental") not skipped for "bootstrap::test::Incremental" -- not in ["src/tools/tidy"]
2020-04-06T14:04:58.5111234Z Check compiletest suite=incremental mode=incremental (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
---
2020-04-06T14:05:21.2214127Z Suite("src/test/debuginfo") not skipped for "bootstrap::test::Debuginfo" -- not in ["src/tools/tidy"]
2020-04-06T14:05:21.2465180Z Check compiletest suite=debuginfo mode=debuginfo (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
2020-04-06T14:05:21.4570959Z 
2020-04-06T14:05:21.4571301Z running 115 tests
2020-04-06T14:05:36.2634751Z iiiii..i.....i..i...i..i.i.i..i..i..ii....i.i....ii..........iiii.........i.....i..i.......ii.i.ii.. 100/115
2020-04-06T14:05:38.2322508Z ...iiii.....ii.
2020-04-06T14:05:38.2323785Z 
2020-04-06T14:05:38.2327335Z  finished in 16.986
2020-04-06T14:05:38.2343028Z Suite("src/test/ui-fulldeps") not skipped for "bootstrap::test::UiFullDeps" -- not in ["src/tools/tidy"]
2020-04-06T14:05:38.2343821Z Uplifting stage1 rustc (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
---
2020-04-06T14:19:39.0322422Z 
2020-04-06T14:19:39.0325002Z    Doc-tests core
2020-04-06T14:19:44.2992691Z 
2020-04-06T14:19:44.2993500Z running 2508 tests
2020-04-06T14:19:54.3464955Z ......iiiii......................................................................................... 100/2508
2020-04-06T14:20:04.0879261Z .....................................................................................ii............. 200/2508
2020-04-06T14:20:26.9977062Z ....................i............................................................................... 400/2508
2020-04-06T14:20:26.9977062Z ....................i............................................................................... 400/2508
2020-04-06T14:20:37.7891244Z ..........................................................................i..i..................iiii 500/2508
2020-04-06T14:20:55.2135610Z ............................................................................FF.F.................... 700/2508
2020-04-06T14:21:04.1377157Z ......................................................FF.F.......................................... 800/2508
2020-04-06T14:21:13.1238896Z ................................FF.F................................................................ 900/2508
2020-04-06T14:21:13.1238896Z ................................FF.F................................................................ 900/2508
2020-04-06T14:21:21.9040036Z ..........FF.F..........................................................................F.FF........ 1000/2508
2020-04-06T14:21:30.8160504Z ..................................................................FF.F.............................. 1100/2508
2020-04-06T14:21:49.2434583Z .................................................................................................... 1300/2508
2020-04-06T14:21:58.3052397Z .................................................................................................... 1400/2508
2020-04-06T14:22:07.5136072Z .................................................................................................... 1500/2508
2020-04-06T14:22:16.6644141Z .................................................................................................... 1600/2508
---
2020-04-06T14:23:46.6091587Z 
2020-04-06T14:23:46.6092165Z error[E0658]: use of unstable library feature 'int_log'
2020-04-06T14:23:46.6092584Z  --> num/mod.rs:56:19
2020-04-06T14:23:46.6092768Z   |
2020-04-06T14:23:46.6092940Z 9 | let result = five.log(5);
2020-04-06T14:23:46.6093486Z   |
2020-04-06T14:23:46.6093486Z   |
2020-04-06T14:23:46.6093729Z   = help: add `#![feature(int_log)]` to the crate attributes to enable
2020-04-06T14:23:46.6094129Z error: aborting due to 2 previous errors
2020-04-06T14:23:46.6094311Z 
2020-04-06T14:23:46.6094725Z For more information about this error, try `rustc --explain E0658`.
2020-04-06T14:23:46.6095137Z Couldn't compile the test.
---
2020-04-06T14:23:46.6098439Z 
2020-04-06T14:23:46.6098843Z error[E0658]: use of unstable library feature 'int_log'
2020-04-06T14:23:46.6099236Z  --> num/mod.rs:52:18
2020-04-06T14:23:46.6099393Z   |
2020-04-06T14:23:46.6099577Z 9 | let result = ten.log10();
2020-04-06T14:23:46.6100148Z   |
2020-04-06T14:23:46.6100148Z   |
2020-04-06T14:23:46.6100411Z   = help: add `#![feature(int_log)]` to the crate attributes to enable
2020-04-06T14:23:46.6100809Z error: aborting due to 2 previous errors
2020-04-06T14:23:46.6100972Z 
2020-04-06T14:23:46.6101444Z For more information about this error, try `rustc --explain E0658`.
2020-04-06T14:23:46.6101858Z Couldn't compile the test.
---
2020-04-06T14:23:46.6106148Z   |
2020-04-06T14:23:46.6106306Z 9 | let result = two.log2();
2020-04-06T14:23:46.6106520Z   |                  ^^^^
2020-04-06T14:23:46.6106791Z   |
2020-04-06T14:23:46.6107035Z   = help: add `#![feature(int_log)]` to the crate attributes to enable
2020-04-06T14:23:46.6107438Z error: aborting due to 2 previous errors
2020-04-06T14:23:46.6107597Z 
2020-04-06T14:23:46.6108032Z For more information about this error, try `rustc --explain E0658`.
2020-04-06T14:23:46.6108454Z Couldn't compile the test.
---
2020-04-06T14:23:46.6112018Z 
2020-04-06T14:23:46.6112417Z error[E0658]: use of unstable library feature 'int_log'
2020-04-06T14:23:46.6114759Z  --> num/mod.rs:56:19
2020-04-06T14:23:46.6115331Z   |
2020-04-06T14:23:46.6115508Z 9 | let result = five.log(5);
2020-04-06T14:23:46.6117430Z   |
2020-04-06T14:23:46.6117430Z   |
2020-04-06T14:23:46.6117674Z   = help: add `#![feature(int_log)]` to the crate attributes to enable
2020-04-06T14:23:46.6118093Z error: aborting due to 2 previous errors
2020-04-06T14:23:46.6118269Z 
2020-04-06T14:23:46.6118782Z For more information about this error, try `rustc --explain E0658`.
2020-04-06T14:23:46.6119198Z Couldn't compile the test.
---
2020-04-06T14:23:46.6122509Z 
2020-04-06T14:23:46.6122891Z error[E0658]: use of unstable library feature 'int_log'
2020-04-06T14:23:46.6123444Z  --> num/mod.rs:52:18
2020-04-06T14:23:46.6123605Z   |
2020-04-06T14:23:46.6123796Z 9 | let result = ten.log10();
2020-04-06T14:23:46.6124158Z   |
2020-04-06T14:23:46.6124158Z   |
2020-04-06T14:23:46.6124420Z   = help: add `#![feature(int_log)]` to the crate attributes to enable
2020-04-06T14:23:46.6124817Z error: aborting due to 2 previous errors
2020-04-06T14:23:46.6124988Z 
2020-04-06T14:23:46.6125420Z For more information about this error, try `rustc --explain E0658`.
2020-04-06T14:23:46.6125832Z Couldn't compile the test.
---
2020-04-06T14:23:46.6135968Z   |
2020-04-06T14:23:46.6136151Z 9 | let result = two.log2();
2020-04-06T14:23:46.6136397Z   |                  ^^^^
2020-04-06T14:23:46.6136567Z   |
2020-04-06T14:23:46.6136827Z   = help: add `#![feature(int_log)]` to the crate attributes to enable
2020-04-06T14:23:46.6137277Z error: aborting due to 2 previous errors
2020-04-06T14:23:46.6137789Z 
2020-04-06T14:23:46.6138308Z For more information about this error, try `rustc --explain E0658`.
2020-04-06T14:23:46.6138746Z Couldn't compile the test.
---
2020-04-06T14:23:46.6148821Z 
2020-04-06T14:23:46.6149266Z error[E0658]: use of unstable library feature 'int_log'
2020-04-06T14:23:46.6149873Z  --> num/mod.rs:56:19
2020-04-06T14:23:46.6150039Z   |
2020-04-06T14:23:46.6150213Z 9 | let result = five.log(5);
2020-04-06T14:23:46.6150615Z   |
2020-04-06T14:23:46.6150615Z   |
2020-04-06T14:23:46.6150868Z   = help: add `#![feature(int_log)]` to the crate attributes to enable
2020-04-06T14:23:46.6151299Z error: aborting due to 2 previous errors
2020-04-06T14:23:46.6151470Z 
2020-04-06T14:23:46.6151899Z For more information about this error, try `rustc --explain E0658`.
2020-04-06T14:23:46.6152324Z Couldn't compile the test.
---
2020-04-06T14:23:46.6156047Z 
2020-04-06T14:23:46.6156469Z error[E0658]: use of unstable library feature 'int_log'
2020-04-06T14:23:46.6156859Z  --> num/mod.rs:52:18
2020-04-06T14:23:46.6157035Z   |
2020-04-06T14:23:46.6157383Z 9 | let result = ten.log10();
2020-04-06T14:23:46.6157760Z   |
2020-04-06T14:23:46.6157760Z   |
2020-04-06T14:23:46.6158195Z   = help: add `#![feature(int_log)]` to the crate attributes to enable
2020-04-06T14:23:46.6158595Z error: aborting due to 2 previous errors
2020-04-06T14:23:46.6158760Z 
2020-04-06T14:23:46.6159195Z For more information about this error, try `rustc --explain E0658`.
2020-04-06T14:23:46.6159793Z Couldn't compile the test.
---
2020-04-06T14:23:46.6166199Z   |
2020-04-06T14:23:46.6166395Z 9 | let result = two.log2();
2020-04-06T14:23:46.6166614Z   |                  ^^^^
2020-04-06T14:23:46.6166782Z   |
2020-04-06T14:23:46.6167043Z   = help: add `#![feature(int_log)]` to the crate attributes to enable
2020-04-06T14:23:46.6167653Z error: aborting due to 2 previous errors
2020-04-06T14:23:46.6167833Z 
2020-04-06T14:23:46.6168266Z For more information about this error, try `rustc --explain E0658`.
2020-04-06T14:23:46.6168708Z Couldn't compile the test.
---
2020-04-06T14:23:46.6173367Z 
2020-04-06T14:23:46.6173825Z error[E0658]: use of unstable library feature 'int_log'
2020-04-06T14:23:46.6174280Z  --> num/mod.rs:56:19
2020-04-06T14:23:46.6174449Z   |
2020-04-06T14:23:46.6174626Z 9 | let result = five.log(5);
2020-04-06T14:23:46.6175031Z   |
2020-04-06T14:23:46.6175031Z   |
2020-04-06T14:23:46.6175595Z   = help: add `#![feature(int_log)]` to the crate attributes to enable
2020-04-06T14:23:46.6176046Z error: aborting due to 2 previous errors
2020-04-06T14:23:46.6176229Z 
2020-04-06T14:23:46.6176692Z For more information about this error, try `rustc --explain E0658`.
2020-04-06T14:23:46.6177151Z Couldn't compile the test.
---
2020-04-06T14:23:46.6181697Z 
2020-04-06T14:23:46.6182201Z error[E0658]: use of unstable library feature 'int_log'
2020-04-06T14:23:46.6182630Z  --> num/mod.rs:52:18
2020-04-06T14:23:46.6182821Z   |
2020-04-06T14:23:46.6183000Z 9 | let result = ten.log10();
2020-04-06T14:23:46.6183410Z   |
2020-04-06T14:23:46.6183410Z   |
2020-04-06T14:23:46.6183670Z   = help: add `#![feature(int_log)]` to the crate attributes to enable
2020-04-06T14:23:46.6184101Z error: aborting due to 2 previous errors
2020-04-06T14:23:46.6184299Z 
2020-04-06T14:23:46.6184769Z For more information about this error, try `rustc --explain E0658`.
2020-04-06T14:23:46.6185218Z Couldn't compile the test.
---
2020-04-06T14:23:46.6189958Z   |
2020-04-06T14:23:46.6190153Z 9 | let result = two.log2();
2020-04-06T14:23:46.6190371Z   |                  ^^^^
2020-04-06T14:23:46.6190548Z   |
2020-04-06T14:23:46.6190825Z   = help: add `#![feature(int_log)]` to the crate attributes to enable
2020-04-06T14:23:46.6191255Z error: aborting due to 2 previous errors
2020-04-06T14:23:46.6191431Z 
2020-04-06T14:23:46.6191888Z For more information about this error, try `rustc --explain E0658`.
2020-04-06T14:23:46.6192329Z Couldn't compile the test.
---
2020-04-06T14:23:46.6195830Z 
2020-04-06T14:23:46.6196241Z error[E0658]: use of unstable library feature 'int_log'
2020-04-06T14:23:46.6196682Z  --> num/mod.rs:56:19
2020-04-06T14:23:46.6196852Z   |
2020-04-06T14:23:46.6197031Z 9 | let result = five.log(5);
2020-04-06T14:23:46.6197602Z   |
2020-04-06T14:23:46.6197602Z   |
2020-04-06T14:23:46.6198043Z   = help: add `#![feature(int_log)]` to the crate attributes to enable
2020-04-06T14:23:46.6198493Z error: aborting due to 2 previous errors
2020-04-06T14:23:46.6198671Z 
2020-04-06T14:23:46.6199347Z For more information about this error, try `rustc --explain E0658`.
2020-04-06T14:23:46.6199878Z Couldn't compile the test.
---
2020-04-06T14:23:46.6207446Z 
2020-04-06T14:23:46.6208014Z error[E0658]: use of unstable library feature 'int_log'
2020-04-06T14:23:46.6208442Z  --> num/mod.rs:52:18
2020-04-06T14:23:46.6208633Z   |
2020-04-06T14:23:46.6208811Z 9 | let result = ten.log10();
2020-04-06T14:23:46.6209223Z   |
2020-04-06T14:23:46.6209223Z   |
2020-04-06T14:23:46.6209485Z   = help: add `#![feature(int_log)]` to the crate attributes to enable
2020-04-06T14:23:46.6210962Z error: aborting due to 2 previous errors
2020-04-06T14:23:46.6211171Z 
2020-04-06T14:23:46.6211679Z For more information about this error, try `rustc --explain E0658`.
2020-04-06T14:23:46.6212124Z Couldn't compile the test.
---
2020-04-06T14:23:46.6217845Z   |
2020-04-06T14:23:46.6218043Z 9 | let result = two.log2();
2020-04-06T14:23:46.6218263Z   |                  ^^^^
2020-04-06T14:23:46.6218431Z   |
2020-04-06T14:23:46.6218713Z   = help: add `#![feature(int_log)]` to the crate attributes to enable
2020-04-06T14:23:46.6219143Z error: aborting due to 2 previous errors
2020-04-06T14:23:46.6219321Z 
2020-04-06T14:23:46.6219786Z For more information about this error, try `rustc --explain E0658`.
2020-04-06T14:23:46.6220231Z Couldn't compile the test.
---
2020-04-06T14:23:46.6223769Z 
2020-04-06T14:23:46.6228898Z error[E0658]: use of unstable library feature 'int_log'
2020-04-06T14:23:46.6229426Z  --> num/mod.rs:56:19
2020-04-06T14:23:46.6229611Z   |
2020-04-06T14:23:46.6229791Z 9 | let result = five.log(5);
2020-04-06T14:23:46.6230341Z   |
2020-04-06T14:23:46.6230341Z   |
2020-04-06T14:23:46.6230607Z   = help: add `#![feature(int_log)]` to the crate attributes to enable
2020-04-06T14:23:46.6237499Z error: aborting due to 2 previous errors
2020-04-06T14:23:46.6237945Z 
2020-04-06T14:23:46.6241574Z For more information about this error, try `rustc --explain E0658`.
2020-04-06T14:23:46.6242968Z Couldn't compile the test.
---
2020-04-06T14:23:46.6247068Z 
2020-04-06T14:23:46.6247455Z error[E0658]: use of unstable library feature 'int_log'
2020-04-06T14:23:46.6247864Z  --> num/mod.rs:52:18
2020-04-06T14:23:46.6248021Z   |
2020-04-06T14:23:46.6248188Z 9 | let result = ten.log10();
2020-04-06T14:23:46.6248575Z   |
2020-04-06T14:23:46.6248575Z   |
2020-04-06T14:23:46.6248819Z   = help: add `#![feature(int_log)]` to the crate attributes to enable
2020-04-06T14:23:46.6249236Z error: aborting due to 2 previous errors
2020-04-06T14:23:46.6249402Z 
2020-04-06T14:23:46.6255613Z For more information about this error, try `rustc --explain E0658`.
2020-04-06T14:23:46.6256231Z Couldn't compile the test.
---
2020-04-06T14:23:46.6263017Z   |
2020-04-06T14:23:46.6263190Z 9 | let result = two.log2();
2020-04-06T14:23:46.6263399Z   |                  ^^^^
2020-04-06T14:23:46.6263561Z   |
2020-04-06T14:23:46.6263837Z   = help: add `#![feature(int_log)]` to the crate attributes to enable
2020-04-06T14:23:46.6264253Z error: aborting due to 2 previous errors
2020-04-06T14:23:46.6264424Z 
2020-04-06T14:23:46.6264866Z For more information about this error, try `rustc --explain E0658`.
2020-04-06T14:23:46.6265289Z Couldn't compile the test.
---
2020-04-06T14:23:46.6278495Z 
2020-04-06T14:23:46.6279586Z failed to run: /checkout/obj/build/bootstrap/debug/bootstrap test --exclude src/tools/tidy
2020-04-06T14:23:46.6280007Z Build completed unsuccessfully in 1:30:30
2020-04-06T14:23:46.6280271Z == clock drift check ==
2020-04-06T14:23:46.6280547Z   local time: Mon Apr  6 14:23:46 UTC 2020
2020-04-06T14:23:46.7996478Z   network time: Mon, 06 Apr 2020 14:23:46 GMT
2020-04-06T14:23:47.4173300Z 
2020-04-06T14:23:47.4173300Z 
2020-04-06T14:23:47.4254929Z ##[error]Bash exited with code '1'.
2020-04-06T14:23:47.4272205Z ##[section]Finishing: Run build
2020-04-06T14:23:47.4321087Z ##[section]Starting: Checkout rust-lang/rust@refs/pull/70835/merge to s
2020-04-06T14:23:47.4326326Z Task         : Get sources
2020-04-06T14:23:47.4326658Z Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
2020-04-06T14:23:47.4326983Z Version      : 1.0.0
2020-04-06T14:23:47.4327196Z Author       : Microsoft
2020-04-06T14:23:47.4327196Z Author       : Microsoft
2020-04-06T14:23:47.4327562Z Help         : [More Information](https://go.microsoft.com/fwlink/?LinkId=798199)
2020-04-06T14:23:47.4327974Z ==============================================================================
2020-04-06T14:23:47.7943366Z Cleaning any cached credential from repository: rust-lang/rust (GitHub)
2020-04-06T14:23:47.7995213Z ##[section]Finishing: Checkout rust-lang/rust@refs/pull/70835/merge to s
2020-04-06T14:23:47.8096434Z Cleaning up task key
2020-04-06T14:23:47.8099027Z Start cleaning up orphan processes.
2020-04-06T14:23:47.8309196Z Terminate orphan process: pid (3594) (python)
2020-04-06T14:23:47.8509018Z ##[section]Finishing: Finalize Job

I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact @rust-lang/infra. (Feature Requests)

@yoshuawuyts
Copy link
Member Author

So that is a use case for log2, but not for a general base, so I still cannot think of a use case for non-base-2 log of an integer returning an integer.

I'm glad we've been able to agree on the validity of using log2 for integers. As I mentioned in my opening post I'm not big on math so I can't provide elaborate explainers for uses of various log bases. But looking at the Wikipedia entry for logarithms one of the first examples is as follows:

In the simplest case, the logarithm counts the number of occurrences of the same factor in repeated multiplication; e.g., since 1000 = 10 × 10 × 10 = 103, the "logarithm base 10" of 1000 is 3, or log10(1000) = 3.

What they're describing here is log10. This seems useful to answer questions such as: "How many digits does a given integer have?" In a best-case scenario having log10 available on integers is easier to write than having to cast to floats and back. But when dealing with large numbers it might be a worst case scenario, and using floats may be too small to contain the operation and overflow.

My understanding is that various encodings such as hex, base32, and base64 may want to calculate similar things as we do in the example for base10. Doing the math by going through base2 seems possible here, but as I shared in my opening post converting bases using integers is susceptible to rounding errors. Instead being able to express the base directly in a method seems like a better way to go about it.

Finally I think it's generally a good idea to maintain API consistency where possible. Floats have access to {base,base2,base10}. It would only seem natural that if we consider base2 to be a useful addition to integers, we should provide base and base10 as well.

@tspiteri
Copy link
Contributor

tspiteri commented Apr 6, 2020

Thanks, I find the example of using log10 for the number of decimal digits quite instructive: u.log10().unwrap_or(0) + 1 for unsigned u. It is way less tricky and error prone than finding the log by converting to f64, where for example 9_999_999_999_999_998_976_u64 as f64 would become 10_000_000_000_000_000_000_f64 .

src/libcore/num/mod.rs Outdated Show resolved Hide resolved
src/libcore/tests/num/int_log.rs Outdated Show resolved Hide resolved
src/libcore/num/mod.rs Outdated Show resolved Hide resolved
src/libcore/tests/num/int_log.rs Outdated Show resolved Hide resolved
src/libcore/tests/num/int_log.rs Outdated Show resolved Hide resolved
@tspiteri
Copy link
Contributor

tspiteri commented Apr 6, 2020

An idea about log10. Instead of the current implementation, I came up with another implementation that makes use of log2 and pow. Its performance is still logarithmic, but log2 is pretty fast and pow uses multiplication instead of division. Though I'm not good at writing benchmarks, some rudimentary benchmarks I tried showed improvement.

pub fn log10(self) -> Option<Self> {
    if val <= 0 {
        return None;
    }
    let size = mem::size_of::<Self>() as u32;
    let log2 = size * 8 - 1 - self.leading_zeros();

    // Since log2 < 128, multiplying log2 by LOG10_2_TIMES_2_TO_26 never overflows.
    // (log10 <= 38, which needs up to 6 bits, leaving 26 bits free in u32.)
    const LOG10_2_TIMES_2_TO_26: u32 = 20_201_781;
    let lower_bound = (log2 * LOG10_2_TIMES_2_TO_26) >> 26;
    let ten_to_lower_bound = Self::pow(10, lower_bound);
    if ten_to_lower_bound < Self::MAX / 10 && self >= ten_to_lower_bound * 10 {
        Some(lower_bound as Self + 1)
    } else {
        Some(lower_bound as Self)
    }
}

@yoshuawuyts
Copy link
Member Author

@tspiteri a useful measure would be to run it through compiler explorer and compare the ASM. Here's a link comparing the assembly of both versions: link (your version is shorter and doesn't seem to rely on recursion).

However I'm having some trouble making it generic for all integer types; only u32 seems to work. Could you verify with e.g. u8/u64 that it works as intended?

@tspiteri
Copy link
Contributor

tspiteri commented Apr 6, 2020

Argh, I had previously manually inlined log2 without the conversion to T, and got that working, then I edited the code to use self.log2()? without testing, but that is not of type u32 like the constant and the requirement of pow. I reedited the code, and updated the code in compiler explorer to u64.

src/libcore/num/mod.rs Outdated Show resolved Hide resolved
@yoshuawuyts
Copy link
Member Author

@tspiteri I'm still having some trouble integrating your code for all number types. Perhaps it'd be an idea if we can land this PR using self.log(10) as the algorithm for Integer::log10, and then optimizing it can be done in a follow-up PR.

I've opened a tracking issue in #70887 and marked "optimize log10 implementation" as a todo.

@cuviper
Copy link
Member

cuviper commented Apr 7, 2020

There's some discussion of integer log 10 on Bit Twiddling Hacks:
http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10

@tspiteri
Copy link
Contributor

tspiteri commented Apr 7, 2020

@cuviper That is quite similar to the function I wrote, with the major difference being that it uses a lookup table instead of pow(10, n). The code shows a 40-byte lookup table for u32 (4 bytes times 10, which is ceil log10 232). For u128 the table would grow to 624 bytes (16 bytes times 39, which is ceil log10 2128).

There are two other minor differences: I used floor and optionally increased one, the bit hack uses ceil and optionally decreases one; and I used 20201472 >> 26, the bit hack uses 1233 >> 12.

@scottmcm
Copy link
Member

scottmcm commented Apr 9, 2020

Every single time I've used leading_zeros it was a place where I'd rather have used ilog2, so I'd certainly be interested in the simple part of this. One thing to ponder: would it make sense to put ilog2 on NonZeroU32 and friends, to avoid the "what's the answer for zero?" question? That would let it avoid the <= 0 check, and thus not have to return an Option, and could internally use the otherwise-never-exposed ctlz_nonzero intrinsic for nicer codegen.

That said, it's not obvious to me that that log10 methods and such belong in core, compared to a more math-focused crate. But we'll see what libs has to say.

@yoshuawuyts
Copy link
Member Author

@scottmcm I've filed a note on exploring log methods for NonZeroInt types in the tracking issue #70887.

@tspiteri
Copy link
Contributor

tspiteri commented Apr 9, 2020

@scottmcm The log2 function already can use ctlz_nonzero as it is in the branch where self ≥ 1, similar to how ctlz_nonzero is used in the private method one_less_than_next_power_of_two.

@yoshuawuyts I think one_less_than_next_power_of_two is a private method similar to ilog, so there is the precedent you mentioned in the original comment, I'm guessing you didn't find it as it is only in unsigned primitives, not in signed primitives.

@tspiteri
Copy link
Contributor

tspiteri commented Apr 9, 2020

@yoshuawuyts I've just realized that the log functions are only defined in the int_impl macro, not in the uint_impl macro, which means that they are only defined for signed integers and not for unsigned integers.

That also led me to realize that the tests are not running, otherwise this would have been caught. I think you need to include a line mod int_log; inside src/libcore/tests/num/mod.rs.

@programmerjake
Copy link
Member

a perhaps more easy to understand ilog2 implementation would be:

fn ilog2(self) -> Option<u32> {
    if self <= 0 {
        None
    } else {
        (1 as Self).leading_zeros() - self.leading_zeros()
    }
}

@EdorianDark
Copy link
Contributor

While I support adding log functions to integers, I am confused, why the log functions return an integer.
Mathematically this is not true:

assert_eq!(999u32.log(10), Some(2));

These functions should return a floating point number like they do in C++ .

@yoshuawuyts
Copy link
Member Author

These functions should return a floating point number like they do in C++ .

@EdorianDark the C++ docs you linked show floating point log functions. They take a float (float, double, long double) and return a float of the same size. Given that integer logarithm never produces a result with a decimal value (this is by design), the right representation for the return type is an integer.

@EdorianDark
Copy link
Contributor

@yoshuawuyts There are several overloads for clamp including the following:

double      log ( IntegralType arg );

Integral types are char, int and any implementation-defined extended integer types, including any signed, unsigned

@tspiteri
Copy link
Contributor

This PR is quite different in motivation from the C++ functions mentioned on integral types (even if there is a link in the original PR comment); the C++ functions work by casting the number to double and then finding the logs. For example, with both g++ and clang++, I get the same result for log10(9999999999999998972UL) and log10(10000000000000000000UL). In fact even the cppreference page states: “4) A set of overloads or a function template accepting an argument of any integral type. Equivalent to 2) (the argument is cast to double).”

Now since they return double, the error is within the rounding error of floats. But if I understand well, the motivation of this PR is to get the correct result when returning the integer log, so it is quite different. And the equivalent of the C++ functions taking integral arguments would remain for example (i as f64).log10(), that is the conversion would have to be explicit, which is more in line with Rust where you cannot for example add 12f64 + 12u64.

@EdorianDark
Copy link
Contributor

EdorianDark commented Apr 10, 2020

I would find it very surprising, if a function called log would not behave like the well known math function.
If the PR refers to a different concept, the functions should be named differently like ilog.

@yoshuawuyts
Copy link
Member Author

yoshuawuyts commented Apr 10, 2020

@EdorianDark I've filed a note on whether the methods should instead be called Integer::{ilog,ilog2,ilog10} in the tracking issue #70887.

edit: I think Integer::{log,log2,log10} have the right names since all other methods on integers aren't prefixed with i either. For example Integer::div isn't called Integer::idiv yet still performs "integer division" which yields different results than if the numbers were cast to floats and then divided.

@bors
Copy link
Contributor

bors commented Jul 6, 2020

💔 Test failed - checks-actions

@bors bors added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Jul 6, 2020
@yoshuawuyts
Copy link
Member Author

It seems the bors merge failed -- I'm unsure how to identify the problem / how to resolve this?

@Dylan-DPC-zz
Copy link

looks spurious

@bors retry

@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 9, 2020
@yoshuawuyts
Copy link
Member Author

@Dylan-DPC hmm, failed again

@Dylan-DPC-zz
Copy link

@yoshuawuyts well it is still in the queue :D

Manishearth added a commit to Manishearth/rust that referenced this pull request Jul 9, 2020
Add Integer::checked_{log,log2,log10}

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)
Manishearth added a commit to Manishearth/rust that referenced this pull request Jul 9, 2020
Add Integer::checked_{log,log2,log10}

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)
@Manishearth
Copy link
Member

@bors r-

#74190 (comment)

@bors bors added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Jul 9, 2020
@tspiteri
Copy link
Contributor

tspiteri commented Jul 22, 2020

It appears to me that the failure is because in the failing android test, log2 is an inaccurate approximation instead of an intrinsic.

Both 8192f32.ln() * std::f32::consts::LOG2_E and 8192f64.ln() * std::f64::consts::LOG2_E return 12.999… instead of 13.

@tspiteri
Copy link
Contributor

tspiteri commented Jul 22, 2020

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).

@bors
Copy link
Contributor

bors commented Jul 28, 2020

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

@Dylan-DPC-zz
Copy link

I'm going to close this pr due to inactivity. If you wish, you can create a new pull request with these changes and we can work from there. Thanks for contributing

@Dylan-DPC-zz Dylan-DPC-zz added S-inactive Status: Inactive and waiting on the author. This is often applied to closed PRs. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Aug 13, 2020
JohnTitor added a commit to JohnTitor/rust that referenced this pull request Jul 7, 2021
Add Integer::log variants

_This is another attempt at landing rust-lang#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)
@Dylan-DPC Dylan-DPC removed the S-inactive Status: Inactive and waiting on the author. This is often applied to closed PRs. label Jul 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.