-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Tracking Issue for num_midpoint #110840
Comments
If this is still open for comments, I'd like to support rounding towards the first argument. In binary search, Another reason is for symmetry of step size; see CppCon 2019 talk on C++'s
In other words, they want:
|
I don't find this part persuasive, as ever binary search I've ever seen has had a ≤ b as an invariant, and thus round-to-first and round-to-negative-infinity are the same.
Can you elaborate on the value of "consistent" here? I would have thought that if one needed predictable and consistent steps, they'd use a power-of-two number of quanta, since otherwise each step isn't half the size of the previous one. I guess this boils down to I'm personally still of the opinion that being consistent with And since it was in the PR, not the tracking issue, cc #92048 (comment) where I argued for rounding to -∞ instead of to first. Also, FWIW, cranelift has https://docs.rs/cranelift/latest/cranelift/prelude/trait.InstBuilder.html#method.avg_round for EDIT: I happened to stumble on this post accidentally today https://devblogs.microsoft.com/oldnewthing/20220207-00/?p=106223, which is also about being consistent with |
For x86 targets, wouldn’t it be possible to implement |
@bluebear94 Exactly what assembly instructions is more up to LLVM than to us. For now the |
Why doesn't fn midpoint(a: f32, b: f32) {
((a as f64) + (b as f64) / 2.0) as f32
} I checked this with kani, and for all finite values it is bit-wise identical to the current implementation and they both output NaNs at the same time. NOTE: that there aren't any calls to #[kani::proof]
fn test_midpoint() {
let a = kani::any::<f32>();
let b = kani::any::<f32>();
// my proposed implementation
let c = (a as f64 + b as f64) / 2.0;
let c = c as f32;
let d = a.midpoint(b); // the current std implementation
if c.is_nan() {
assert!(d.is_nan())
} else {
assert_eq!(c, d)
}
} Run kani with this command cargo kani --no-overflow-checks |
@RustyYato As noted in the implementation PR, f64 and f32 is derivated from the libcxx implementation.
Feel free to send a PR changing the implementation (code source). |
This has been verified by kani as a correct optimization see: rust-lang#110840 (comment) The new implementation is branchless, and only differs in which NaN values are produced (if any are produced at all). Which is fine to change. Aside from NaN handling, this implementation produces bitwise identical results to the original implementation.
Alright, I've put up #121062, 🤞 |
Change f32::midpoint to upcast to f64 This has been verified by kani as a correct optimization see: rust-lang#110840 (comment) The new implementation is branchless and only differs in which NaN values are produced (if any are produced at all), which is fine to change. Aside from NaN handling, this implementation produces bitwise identical results to the original implementation. Question: do we need a codegen test for this? I didn't add one, since the original PR rust-lang#92048 didn't have any codegen tests.
Change f32::midpoint to upcast to f64 This has been verified by kani as a correct optimization see: rust-lang#110840 (comment) The new implementation is branchless and only differs in which NaN values are produced (if any are produced at all), which is fine to change. Aside from NaN handling, this implementation produces bitwise identical results to the original implementation. Question: do we need a codegen test for this? I didn't add one, since the original PR rust-lang#92048 didn't have any codegen tests.
This has been verified by kani as a correct optimization see: rust-lang#110840 (comment) The new implementation is branchless, and only differs in which NaN values are produced (if any are produced at all). Which is fine to change. Aside from NaN handling, this implementation produces bitwise identical results to the original implementation. Reintroduce f32-based midpoint on some ARM targets Add tests for large differences in magnitude fix typo switch to a whitelist based on platform make test generic over f32/f64 add wasm to whitelisted target features Co-authored-by: Alphyr <47725341+a1phyr@users.noreply.github.com> switch from target_family to target_arch
This has been verified by kani as a correct optimization see: rust-lang#110840 (comment) The new implementation is branchless, and only differs in which NaN values are produced (if any are produced at all). Which is fine to change. Aside from NaN handling, this implementation produces bitwise identical results to the original implementation. The new implementation is gated on targets that have a fast 64-bit floating point implementation in hardware, and on WASM.
Change f32::midpoint to upcast to f64 This has been verified by kani as a correct optimization see: rust-lang#110840 (comment) The new implementation is branchless and only differs in which NaN values are produced (if any are produced at all), which is fine to change. Aside from NaN handling, this implementation produces bitwise identical results to the original implementation. Question: do we need a codegen test for this? I didn't add one, since the original PR rust-lang#92048 didn't have any codegen tests.
Change f32::midpoint to upcast to f64 This has been verified by kani as a correct optimization see: rust-lang#110840 (comment) The new implementation is branchless and only differs in which NaN values are produced (if any are produced at all), which is fine to change. Aside from NaN handling, this implementation produces bitwise identical results to the original implementation. Question: do we need a codegen test for this? I didn't add one, since the original PR rust-lang#92048 didn't have any codegen tests.
Rollup merge of rust-lang#121062 - RustyYato:f32-midpoint, r=the8472 Change f32::midpoint to upcast to f64 This has been verified by kani as a correct optimization see: rust-lang#110840 (comment) The new implementation is branchless and only differs in which NaN values are produced (if any are produced at all), which is fine to change. Aside from NaN handling, this implementation produces bitwise identical results to the original implementation. Question: do we need a codegen test for this? I didn't add one, since the original PR rust-lang#92048 didn't have any codegen tests.
rust/library/core/src/num/f64.rs Lines 1052 to 1071 in e35364a
I think the first condition could have an Also, The default branch's comment is incorrect too, that operation is the safe one. I have a commit to update it in #127027. |
Round-towards-zero (or round-away-from-zero) satisfies both of those properties so they're not incompatible. |
Instead of towards negative infinity as is currently the case. This done so that the obvious expectations of `midpoint(a, b) == midpoint(b, a)` and `midpoint(-a, -b) == -midpoint(a, b)` are true, which makes the even more obvious implementation `(a + b) / 2` true. rust-lang#110840 (comment)
…r=dtolnay Round negative signed integer towards zero in `iN::midpoint` This PR changes the implementation of `iN::midpoint` (the signed variants) to round negative signed integers **towards zero** *instead* of negative infinity as is currently the case. This is done so that the obvious expectations[^1] of `midpoint(a, b) == midpoint(b, a)` and `midpoint(-a, -b) == -midpoint(a, b)` are true, which makes the even more obvious implementation `(a + b) / 2` always true. The unsigned variants `uN::midpoint` (which are being [FCP-ed](rust-lang#131784 (comment))) already rounds towards zero, so there is no consistency issue. cc `@scottmcm` r? `@dtolnay` [^1]: rust-lang#110840 (comment)
Stabilize unsigned and float variants of `num_midpoint` feature This PR proposes that we stabilize the unsigned variants of the [`num_midpoint`](rust-lang#110840 (comment)) feature as well as the floats variants, since they are not subject to any unresolved questions, which is equivalent to doing `(a + b) / 2` (and `(a + b) >> 1`) in a sufficiently large number. The stabilized API surface would be: ```rust /// Calculates the middle point of `self` and `rhs`. /// /// `midpoint(a, b)` is `(a + b) / 2` as if it were performed in a sufficiently-large unsigned integral type. /// This implies that the result is always rounded towards negative infinity and that no overflow will ever occur. impl u{8,16,32,64,128,size} { pub const fn midpoint(self, rhs: Self) -> Self; } impl NonZeroU{8,16,32,64,size} { pub const fn midpoint(self, rhs: Self) -> Self; } impl f{32,64} { pub const fn midpoint(self, rhs: Self) -> Self; } ``` The signed variants `u{8,16,32,64,128,size}` would remain gated, until a decision is made about the rounding mode, in other words that the [unresolved questions](rust-lang#110840 (comment)) are resolved. cc `@rust-lang/libs-api` cc `@scottmcm` r? libs-api
Stabilize unsigned and float variants of `num_midpoint` feature This PR proposes that we stabilize the unsigned variants of the [`num_midpoint`](rust-lang#110840 (comment)) feature as well as the floats variants, since they are not subject to any unresolved questions, which is equivalent to doing `(a + b) / 2` (and `(a + b) >> 1`) in a sufficiently large number. The stabilized API surface would be: ```rust /// Calculates the middle point of `self` and `rhs`. /// /// `midpoint(a, b)` is `(a + b) / 2` as if it were performed in a sufficiently-large unsigned integral type. /// This implies that the result is always rounded towards negative infinity and that no overflow will ever occur. impl u{8,16,32,64,128,size} { pub const fn midpoint(self, rhs: Self) -> Self; } impl NonZeroU{8,16,32,64,size} { pub const fn midpoint(self, rhs: Self) -> Self; } impl f{32,64} { pub const fn midpoint(self, rhs: Self) -> Self; } ``` The signed variants `u{8,16,32,64,128,size}` would remain gated, until a decision is made about the rounding mode, in other words that the [unresolved questions](rust-lang#110840 (comment)) are resolved. cc `@rust-lang/libs-api` cc `@scottmcm` r? libs-api
Rollup merge of rust-lang#131784 - Urgau:stabilize-midpoint, r=dtolnay Stabilize unsigned and float variants of `num_midpoint` feature This PR proposes that we stabilize the unsigned variants of the [`num_midpoint`](rust-lang#110840 (comment)) feature as well as the floats variants, since they are not subject to any unresolved questions, which is equivalent to doing `(a + b) / 2` (and `(a + b) >> 1`) in a sufficiently large number. The stabilized API surface would be: ```rust /// Calculates the middle point of `self` and `rhs`. /// /// `midpoint(a, b)` is `(a + b) / 2` as if it were performed in a sufficiently-large unsigned integral type. /// This implies that the result is always rounded towards negative infinity and that no overflow will ever occur. impl u{8,16,32,64,128,size} { pub const fn midpoint(self, rhs: Self) -> Self; } impl NonZeroU{8,16,32,64,size} { pub const fn midpoint(self, rhs: Self) -> Self; } impl f{32,64} { pub const fn midpoint(self, rhs: Self) -> Self; } ``` The signed variants `u{8,16,32,64,128,size}` would remain gated, until a decision is made about the rounding mode, in other words that the [unresolved questions](rust-lang#110840 (comment)) are resolved. cc `@rust-lang/libs-api` cc `@scottmcm` r? libs-api
Feature gate:
#[feature(num_midpoint)]
,#[feature(num_midpoint_signed)]
and#![feature(const_num_midpoint)]
This is a tracking issue for the midpoint function to
{u,i}{8,16,32,64,128,size}
,NonZeroU{8,16,32,64,size}
andf{32,64}
.The midpoint function calculates the middle point of
lhs
andrhs
.Public API
Steps / History
num_midpoint
feature #131784 (comment)num_midpoint_signed
feature #134340num_midpoint
feature #131784num_midpoint_signed
feature #134340Unresolved Questions
negative infinityzero): Add midpoint function for all integers and floating numbers #92048 (comment)iN::midpoint
#132191 we round towards zero, as to be consistent with the obvious impl(a + b) / 2
.Whether it's commutative (some people wantmidpoint(a, b) == midpoint(b, a)
, but others want a non-commutative definition)Footnotes
https://std-dev-guide.rust-lang.org/feature-lifecycle/stabilization.html ↩
The text was updated successfully, but these errors were encountered: