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

Document round-off error in .mod_euc()-method, see issue #50179 #50342

Merged
merged 3 commits into from
Jun 28, 2018

Conversation

clamydo
Copy link
Contributor

@clamydo clamydo commented Apr 30, 2018

Due to a round-off error the method .mod_euc() of both f32 and f64 can produce mathematical invalid outputs. If self in magnitude is much small than the modulus rhs and negative, self + rhs in the first branch cannot be represented in the given precision and results into rhs. In the mathematical strict sense, this breaks the definition. But given the limitation of floating point arithmetic it can be thought of the closest representable value to the true result, although it is not strictly in the domain [0.0, rhs) of the function. It is rather the left side asymptotical limit. It would be desirable that it produces the mathematical more sound approximation of 0.0, the right side asymptotical limit. But this breaks the property, that self == self.div_euc(rhs) * rhs + a.mod_euc(rhs).

The discussion in issue #50179 did not find an satisfying conclusion to which property is deemed more important. But at least we can document the behaviour. Which this pull request does.

@rust-highfive
Copy link
Collaborator

r? @withoutboats

(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 30, 2018
@rust-highfive

This comment has been minimized.

@clamydo clamydo force-pushed the euclidean branch 2 times, most recently from 47eb521 to 053b564 Compare April 30, 2018 17:39
@@ -254,6 +257,8 @@ impl f32 {
/// assert_eq!((-a).mod_euc(b), 1.0);
/// assert_eq!(a.mod_euc(-b), 3.0);
/// assert_eq!((-a).mod_euc(-b), 1.0);
/// // limitation due to round-off error
/// assert!((-std::f32::EPSILON).mod_euc(3.0) != 0.0);
Copy link
Member

Choose a reason for hiding this comment

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

This has reliable output, right? assuming so, it'd be nice to phrase it positively instead:

assert_eq!((-std::f32::EPSILON).mod_euc(3.0), 3.0);

Also, while you're here, maybe you could talk about how this treats NaNs and Infinities? For example, I see that currently,

assert!((std::f32::INFINITY).mod_euc(3.0).is_nan());

(Did the RFC talk about what's supposed to happen in such cases?)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Floating point arithmetic is many things, but it is deterministic 😉. I could turn this assertion around, but I put it that way ('not equal to') to stress, that it results in something one might not expect. The way proposed by you reads more like expected behaviour.

The RFC did not talk about border cases of floats. I could also add these, if you'd like that:

    assert!(std::f64::INFINITY.mod_euc(a).is_nan());
    assert_eq!(a.mod_euc(std::f64::INFINITY), a);
    assert!(a.mod_euc(std::f64::NAN).is_nan());
    assert!(std::f64::INFINITY.mod_euc(std::f64::INFINITY).is_nan());
    assert!(std::f64::INFINITY.mod_euc(std::f64::NAN).is_nan());
    assert!(std::f64::NAN.mod_euc(std::f64::INFINITY).is_nan());

which all are true as expected. In particular assert_eq!(a.mod_euc(std::f64::INFINITY), a); is nice. The rest might be a bit noisy?

Copy link
Member

Choose a reason for hiding this comment

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

The way proposed by you reads more like expected behaviour.

I don't know what's best. I kind-of like not calling it error, but putting a positive spin on it and saying "yes, this is the closest float to the mathematically-correct value".

Whatever you choose to do, as someone from the "but I thought the whole point was to have a half-open range" camp, I'd like to see enough of the "why this behaviour isn't a bug" discussion in the docs to keep someone from me from opening another issue about it 😆

The rest might be a bit noisy?

Maybe pick some that are particularly interesting for the examples section, describe the usual things like "any input being NaN gives NaN" in prose, and add (non-doc-)tests for the rest? The only such tests I could find for mod_euc so far is this:

#[test]
fn test_mod_euc() {
assert!((-1 as $T).mod_euc(MIN) == MAX);
}

@shepmaster
Copy link
Member

Ping from triage, @withoutboats ! This is awaiting your review.

@pietroalbini pietroalbini added the T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. label May 10, 2018
@pietroalbini
Copy link
Member

Ping from triage! This PR needs a review, can @withoutboats or someone else from @rust-lang/libs review this?

@withoutboats
Copy link
Contributor

someone else from libs please, I don't feel confident to review the accuracy of these comments :)

@alexcrichton
Copy link
Member

@fkjogu did you want to address @scottmcm's comment or is this good to go? The tweaks look fine by me (naively!)

@pietroalbini pietroalbini 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-review Status: Awaiting review from the assignee but also interested parties. labels May 14, 2018
@clamydo
Copy link
Contributor Author

clamydo commented May 15, 2018

I'm going to add the unit tests for the special float values (INF, NAN). i do not think it is necessary to spell it out explicitly: it is expected behavior after all.

But I find it hard to include a rational on choosing the given drawbacks in the documentation, because I'm not fully convinced, this is the way to go for this method.

The reason I haven't done the above already is that ideally I'd like to proof that self == self.div_euc(rhs) * rhs + a.mod_euc(rhs) is always true with the given implementation before adding this property to the documentation as well. I'll update soon.

@shepmaster
Copy link
Member

r? @alexcrichton

@TimNN
Copy link
Contributor

TimNN commented May 22, 2018

Ping from triage, @fkjogu: could you give us an update on your plans for this PR?

@clamydo
Copy link
Contributor Author

clamydo commented May 23, 2018

I've added a remark about the rationale of the implementation, although I haven't proven the property. I just ran some tests on edge cases I came up with.

I've also added some unit tests. Currently this is true

assert!((std::f64::INFINITY.div_euc(a)).is_infinite());

but I'd argue, that 'NAN' would be more correct. It is not completely off, since yes, a number fits into infinity infinity times. But ultimately it is just not defined.

From my point of view, this is good to go.

@rust-highfive
Copy link
Collaborator

The job x86_64-gnu-llvm-3.9 of your PR failed on Travis (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.
[01:03:43] travis_fold:start:test_stage1-core
travis_time:start:test_stage1-core
Testing core stage1 (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
[01:03:43]    Compiling core v0.0.0 (file:///checkout/src/libcore)
[01:03:46] error[E0425]: cannot find value `INFINITY` in this scope
[01:03:46]   --> libcore/../libcore/tests/num/int_macros.rs:36:17
[01:03:46]    |
[01:03:46] 36 |         assert!(INFINITY.mod_euc(a).is_nan());
[01:03:46]    | 
[01:03:46]    | 
[01:03:46]   ::: libcore/../libcore/tests/num/i8.rs:11:1
[01:03:46]    |
[01:03:46] 11 | int_module!(i8, i8);
[01:03:46]    | -------------------- in this macro invocation
[01:03:46] help: possible candidates are found in other modules, you can import them into scope
[01:03:46] 1  | use core::f32::INFINITY;
[01:03:46]    |
[01:03:46] 1  | use core::f64::INFINITY;
[01:03:46]    |
[01:03:46]    |
[01:03:46] 1  | use std::f32::INFINITY;
[01:03:46]    |
[01:03:46] 1  | use std::f64::INFINITY;
[01:03:46]    |
[01:03:46] 
[01:03:46] error[E0425]: cannot find value `a` in this scope
[01:03:46]   --> libcore/../libcore/tests/num/int_macros.rs:36:34
[01:03:46]    |
[01:03:46] 36 |         assert!(INFINITY.mod_euc(a).is_nan());
[01:03:46]    |                                  ^ did you mean `A`?
[01:03:46]    | 
[01:03:46]   ::: libcore/../libcore/tests/num/i8.rs:11:1
[01:03:46]    |
[01:03:46] 11 | int_module!(i8, i8);
[01:03:46]    | -------------------- in this macro invocation
[01:03:46] error[E0425]: cannot find value `a` in this scope
[01:03:46] error[E0425]: cannot find value `a` in this scope
[01:03:46]   --> libcore/../libcore/tests/num/int_macros.rs:37:20
[01:03:46]    |
[01:03:46] 37 |         assert_eq!(a.mod_euc(INFINITY), a);
[01:03:46]    |                    ^ did you mean `A`?
[01:03:46]    | 
[01:03:46]   ::: libcore/../libcore/tests/num/i8.rs:11:1
[01:03:46]    |
[01:03:46] 11 | int_module!(i8, i8);
[01:03:46]    | -------------------- in this macro invocation
[01:03:46] 
[01:03:46] error[E0425]: cannot find value `INFINITY` in this scope
[01:03:46]   --> libcore/../libcore/tests/num/int_macros.rs:37:30
[01:03:46]    |
[01:03:46] 37 |         assert_eq!(a.mod_euc(INFINITY), a);
[01:03:46]    | 
[01:03:46]    | 
[01:03:46]   ::: libcore/../libcore/tests/num/i8.rs:11:1
[01:03:46]    |
[01:03:46] 11 | int_module!(i8, i8);
[01:03:46]    | -------------------- in this macro invocation
[01:03:46] help: possible candidates are found in other modules, you can import them into scope
[01:03:46] 1  | use core::f32::INFINITY;
[01:03:46]    |
[01:03:46] 1  | use core::f64::INFINITY;
[01:03:46]    |
[01:03:46]    |
[01:03:46] 1  | use std::f32::INFINITY;
[01:03:46]    |
[01:03:46] 1  | use std::f64::INFINITY;
[01:03:46]    |
[01:03:46] 
[01:03:46] error[E0425]: cannot find value `a` in this scope
[01:03:46]   --> libcore/../libcore/tests/num/int_macros.rs:37:41
[01:03:46]    |
[01:03:46] 37 |         assert_eq!(a.mod_euc(INFINITY), a);
[01:03:46]    |                                         ^ did you mean `A`?
[01:03:46]    | 
[01:03:46]   ::: libcore/../libcore/tests/num/i8.rs:11:1
[01:03:46]    |
[01:03:46] 11 | int_module!(i8, i8);
[01:03:46]    | -------------------- in this macro invocation
[01:03:46] error[E0425]: cannot find value `a` in this scope
[01:03:46] error[E0425]: cannot find value `a` in this scope
[01:03:46]   --> libcore/../libcore/tests/num/int_macros.rs:38:17
[01:03:46]    |
[01:03:46] 38 |         assert!(a.mod_euc(NAN).is_nan());
[01:03:46]    |                 ^ did you mean `A`?
[01:03:46]    | 
[01:03:46]   ::: libcore/../libcore/tests/num/i8.rs:11:1
[01:03:46]    |
[01:03:46] 11 | int_module!(i8, i8);
[01:03:46]    | -------------------- in this macro invocation
[01:03:46] 
[01:03:46] error[E0425]: cannot find value `NAN` in this scope
[01:03:46]   --> libcore/../libcore/tests/num/int_macros.rs:38:27
[01:03:46]    |
[01:03:46] 38 |         assert!(a.mod_euc(NAN).is_nan());
[01:03:46]    | 
[01:03:46]    | 
[01:03:46]   ::: libcore/../libcore/tests/num/i8.rs:11:1
[01:03:46]    |
[01:03:46] 11 | int_module!(i8, i8);
[01:03:46]    | -------------------- in this macro invocation
[01:03:46] help: possible candidates are found in other modules, you can import them into scope
[01:03:46] 1  | use core::f32::NAN;
[01:03:46]    |
[01:03:46] 1  | use core::f64::NAN;
[01:03:46]    |
[01:03:46]    |
[01:03:46] 1  | use std::f32::NAN;
[01:03:46]    |
[01:03:46] 1  | use std::f64::NAN;
[01:03:46]    |
[01:03:46] 
[01:03:46] error[E0425]: cannot find value `INFINITY` in this scope
[01:03:46]   --> libcore/../libcore/tests/num/int_macros.rs:39:17
[01:03:46]    |
[01:03:46] 39 |         assert!(INFINITY.mod_euc(INFINITY).is_nan());
[01:03:46]    | 
[01:03:46]    | 
[01:03:46]   ::: libcore/../libcore/tests/num/i8.rs:11:1
[01:03:46]    |
[01:03:46] 11 | int_module!(i8, i8);
[01:03:46]    | -------------------- in this macro invocation
[01:03:46] help: possible candidates are found in other modules, you can import them into scope
[01:03:46] 1  | use core::f32::INFINITY;
[01:03:46]    |
[01:03:46] 1  | use core::f64::INFINITY;
[01:03:46]    |
[01:03:46]    |
[01:03:46] 1  | use std::f32::INFINITY;
[01:03:46]    |
[01:03:46] 1  | use std::f64::INFINITY;
[01:03:46]    |
[01:03:46] 
[01:03:46] error[E0425]: cannot find value `INFINITY` in this scope
[01:03:46]   --> libcore/../libcore/tests/num/int_macros.rs:39:34
[01:03:46]    |
[01:03:46] 39 |         assert!(INFINITY.mod_euc(INFINITY).is_nan());
[01:03:46]    | 
[01:03:46]    | 
[01:03:46]   ::: libcore/../libcore/tests/num/i8.rs:11:1
[01:03:46]    |
[01:03:46] 11 | int_module!(i8, i8);
[01:03:46]    | -------------------- in this macro invocation
[01:03:46] help: possible candidates are found in other modules, you can import them into scope
[01:03:46] 1  | use core::f32::INFINITY;
[01:03:46]    |
[01:03:46] 1  | use core::f64::INFINITY;
[01:03:46]    |
[01:03:46]    |
[01:03:46] 1  | use std::f32::INFINITY;
[01:03:46]    |
[01:03:46] 1  | use std::f64::INFINITY;
[01:03:46]    |
[01:03:46] 
[01:03:46] error[E0425]: cannot find value `INFINITY` in this scope
[01:03:46]   --> libcore/../libcore/tests/num/int_macros.rs:40:17
[01:03:46]    |
[01:03:46] 40 |         assert!(INFINITY.mod_euc(NAN).is_nan());
[01:03:46]    | 
[01:03:46]    | 
[01:03:46]   ::: libcore/../libcore/tests/num/i8.rs:11:1
[01:03:46]    |
[01:03:46] 11 | int_module!(i8, i8);
[01:03:46]    | -------------------- in this macro invocation
[01:03:46] help: possible candidates are found in other modules, you can import them into scope
[01:03:46] 1  | use core::f32::INFINITY;
[01:03:46]    |
[01:03:46] 1  | use core::f64::INFINITY;
[01:03:46]    |
[01:03:46]    |
[01:03:46] 1  | use std::f32::INFINITY;
[01:03:46]    |
[01:03:46] 1  | use std::f64::INFINITY;
[01:03:46]    |
[01:03:46] 
[01:03:46] error[E0425]: cannot find value `NAN` in this scope
[01:03:46]   --> libcore/../libcore/tests/num/int_macros.rs:40:34
[01:03:46]    |
[01:03:46] 40 |         assert!(INFINITY.mod_euc(NAN).is_nan());
[01:03:46]    | 
[01:03:46]    | 
[01:03:46]   ::: libcore/../libcore/tests/num/i8.rs:11:1
[01:03:46]    |
[01:03:46] 11 | int_module!(i8, i8);
[01:03:46]    | -------------------- in this macro invocation
[01:03:46] help: possible candidates are found in other modules, you can import them into scope
[01:03:46] 1  | use core::f32::NAN;
[01:03:46]    |
[01:03:46] 1  | use core::f64::NAN;
[01:03:46]    |
[01:03:46]    |
[01:03:46] 1  | use std::f32::NAN;
[01:03:46]    |
[01:03:46] 1  | use std::f64::NAN;
[01:03:46]    |
[01:03:46] 
[01:03:46] error[E0425]: cannot find value `NAN` in this scope
[01:03:46]   --> libcore/../libcore/tests/num/int_macros.rs:41:17
[01:03:46]    |
[01:03:46] 41 |         assert!(NAN.mod_euc(INFINITY).is_nan());
[01:03:46]    | 
[01:03:46]    | 
[01:03:46]   ::: libcore/../libcore/tests/num/i8.rs:11:1
[01:03:46]    |
[01:03:46] 11 | int_module!(i8, i8);
[01:03:46]    | -------------------- in this macro invocation
[01:03:46] help: possible candidates are found in other modules, you can import them into scope
[01:03:46] 1  | use core::f32::NAN;
[01:03:46]    |
[01:03:46] 1  | use core::f64::NAN;
[01:03:46]    |
[01:03:46]    |
[01:03:46] 1  | use std::f32::NAN;
[01:03:46]    |
[01:03:46] 1  | use std::f64::NAN;
[01:03:46]    |
[01:03:46] 
[01:03:46] error[E0425]: cannot find value `INFINITY` in this scope
[01:03:46]   --> libcore/../libcore/tests/num/int_macros.rs:41:29
[01:03:46]    |
[01:03:46] 41 |         assert!(NAN.mod_euc(INFINITY).is_nan());
[01:03:46]    | 
[01:03:46]    | 
[01:03:46]   ::: libcore/../libcore/tests/num/i8.rs:11:1
[01:03:46]    |
[01:03:46] 11 | int_module!(i8, i8);
[01:03:46]    | -------------------- in this macro invocation
[01:03:46] help: possible candidates are found in other modules, you can import them into scope
[01:03:46] 1  | use core::f32::INFINITY;
[01:03:46]    |
[01:03:46] 1  | use core::f64::INFINITY;
[01:03:46]    |
[01:03:46]    |
[01:03:46] 1  | use std::f32::INFINITY;
[01:03:46]    |
[01:03:46] 1  | use std::f64::INFINITY;
[01:03:46]    |
[01:03:46] 
[01:03:46] error[E0425]: cannot find value `a` in this scope
[01:03:46]   --> libcore/../libcore/tests/num/int_macros.rs:46:20
[01:03:46]    |
[01:03:46] 46 |         assert_eq!(a.div_euc(INFINITY), 0.0);
[01:03:46]    |                    ^ did you mean `A`?
[01:03:46]    | 
[01:03:46]   ::: libcore/../libcore/tests/num/i8.rs:11:1
[01:03:46]    |
[01:03:46] 11 | int_module!(i8, i8);
[01:03:46]    | -------------------- in this macro invocation
[01:03:46] 
[01:03:46] error[E0425]: cannot find value `INFINITY` in this scope
[01:03:46]   --> libcore/../libcore/tests/num/int_macros.rs:46:30
[01:03:46]    |
[01:03:46] 46 |         assert_eq!(a.div_euc(INFINITY), 0.0);
[01:03:46]    | 
[01:03:46]    | 
[01:03:46]   ::: libcore/../libcore/tests/num/i8.rs:11:1
[01:03:46]    |
[01:03:46] 11 | int_module!(i8, i8);
[01:03:46]    | -------------------- in this macro invocation
[01:03:46] help: possible candidates are found in other modules, you can import them into scope
[01:03:46] 1  | use core::f32::INFINITY;
[01:03:46]    |
[01:03:46] 1  | use core::f64::INFINITY;
[01:03:46]    |
[01:03:46]    |
[01:03:46] 1  | use std::f32::INFINITY;
[01:03:46]    |
[01:03:46] 1  | use std::f64::INFINITY;
[01:03:46]    |
[01:03:46] 
[01:03:46] error[E0425]: cannot find value `a` in this scope
[01:03:46]   --> libcore/../libcore/tests/num/int_macros.rs:47:17
[01:03:46]    |
[01:03:46] 47 |         assert!(a.div_euc(NAN).is_nan());
[01:03:46]    |                 ^ did you mean `A`?
[01:03:46]    | 
[01:03:46]   ::: libcore/../libcore/tests/num/i8.rs:11:1
[01:03:46]    |
[01:03:46] 11 | int_module!(i8, i8);
[01:03:46]    | -------------------- in this macro invocation
[01:03:46] 
[01:03:46] error[E0425]: cannot find value `NAN` in this scope
[01:03:46]   --> libcore/../libcore/tests/num/int_macros.rs:47:27
[01:03:46]    |
[01:03:46] 47 |         assert!(a.div_euc(NAN).is_nan());
[01:03:46]    | 
[01:03:46]    | 
[01:03:46]   ::: libcore/../libcore/tests/num/i8.rs:11:1
[01:03:46]    |
[01:03:46] 11 | int_module!(i8, i8);
[01:03:46]    | -------------------- in this macro invocation
[01:03:46] help: possible candidates are found in other modules, you can import them into scope
[01:03:46] 1  | use core::f32::NAN;
[01:03:46]    |
[01:03:46] 1  | use core::f64::NAN;
[01:03:46]    |
[01:03:46]    |
[01:03:46] 1  | use std::f32::NAN;
[01:03:46]    |
[01:03:46] 1  | use std::f64::NAN;
[01:03:46]    |
[01:03:46] 
[01:03:46] error[E0425]: cannot find value `INFINITY` in this scope
[01:03:46]   --> libcore/../libcore/tests/num/int_macros.rs:48:17
[01:03:46]    |
[01:03:46] 48 |         assert!(INFINITY.div_euc(INFINITY).is_nan());
[01:03:46]    | 
[01:03:46]    | 
[01:03:46]   ::: libcore/../libcore/tests/num/i8.rs:11:1
[01:03:46]    |
[01:03:46] 11 | int_module!(i8, i8);
[01:03:46]    | -------------------- in this macro invocation
[01:03:46] help: possible candidates are found in other modules, you can import them into scope
[01:03:46] 1  | use core::f32::INFINITY;
[01:03:46]    |
[01:03:46] 1  | use core::f64::INFINITY;
[01:03:46]    |
[01:03:46]    |
[01:03:46] 1  | use std::f32::INFINITY;
[01:03:46]    |
[01:03:46] 1  | use std::f64::INFINITY;
[01:03:46]    |
[01:03:46] 
[01:03:46] error[E0425]: cannot find value `INFINITY` in this scope
[01:03:46]   --> libcore/../libcore/tests/num/int_macros.rs:48:34
[01:03:46]    |
[01:03:46] 48 |         assert!(INFINITY.div_euc(INFINITY).is_nan());
[01:03:46]    | 
[01:03:46]    | 
[01:03:46]   ::: libcore/../libcore/tests/num/i8.rs:11:1
[01:03:46]    |
[01:03:46] 11 | int_module!(i8, i8);
[01:03:46]    | -------------------- in this macro invocation
[01:03:46] help: possible candidates are found in other modules, you can import them into scope
[01:03:46] 1  | use core::f32::INFINITY;
[01:03:46]    |
[01:03:46] 1  | use core::f64::INFINITY;
[01:03:46]    |
[01:03:46]    |
[01:03:46] 1  | use std::f32::INFINITY;
[01:03:46]    |
[01:03:46] 1  | use std::f64::INFINITY;
[01:03:46]    |
[01:03:46] 
[01:03:46] error[E0425]: cannot find value `INFINITY` in this scope
[01:03:46]   --> libcore/../libcore/tests/num/int_macros.rs:49:17
[01:03:46]    |
[01:03:46] 49 |         assert!(INFINITY.div_euc(NAN).is_nan());
[01:03:46]    | 
[01:03:46]    | 
[01:03:46]   ::: libcore/../libcore/tests/num/i8.rs:11:1
[01:03:46]    |
[01:03:46] 11 | int_module!(i8, i8);
[01:03:46]    | -------------------- in this macro invocation
[01:03:46] help: possible candidates are found in other modules, you can import them into scope
[01:03:46] 1  | use core::f32::INFINITY;
[01:03:46]    |
[01:03:46] 1  | use core::f64::INFINITY;
[01:03:46]    |
[01:03:46]    |
[01:03:46] 1  | use std::f32::INFINITY;
[01:03:46]    |
[01:03:46] 1  | use std::f64::INFINITY;
[01:03:46]    |
[01:03:46] 
[01:03:46] error[E0425]: cannot find value `NAN` in this scope
[01:03:46]   --> libcore/../libcore/tests/num/int_macros.rs:49:34
[01:03:46]    |
[01:03:46] 49 |         assert!(INFINITY.div_euc(NAN).is_nan());
[01:03:46]    | 
[01:03:46]    | 
[01:03:46]   ::: libcore/../libcore/tests/num/i8.rs:11:1
[01:03:46]    |
[01:03:46] 11 | int_module!(i8, i8);
[01:03:46]    | -------------------- in this macro invocation
[01:03:46] help: possible candidates are found in other modules, you can import them into scope
[01:03:46] 1  | use core::f32::NAN;
[01:03:46]    |
[01:03:46] 1  | use core::f64::NAN;
[01:03:46]    |
[01:03:46]    |
[01:03:46] 1  | use std::f32::NAN;
[01:03:46]    |
[01:03:46] 1  | use std::f64::NAN;
[01:03:46]    |
[01:03:46] 
[01:03:46] error[E0425]: cannot find value `NAN` in this scope
[01:03:46]   --> libcore/../libcore/tests/num/int_macros.rs:50:17
[01:03:46]    |
[01:03:46] 50 |         assert!(NAN.div_euc(INFINITY).is_nan());
[01:03:46]    | 
[01:03:46]    | 
[01:03:46]   ::: libcore/../libcore/tests/num/i8.rs:11:1
[01:03:46]    |
[01:03:46] 11 | int_module!(i8, i8);
[01:03:46]    | -------------------- in this macro invocation
[01:03:46] help: possible candidates are found in other modules, you can import them into scope
[01:03:46] 1  | use core::f32::NAN;
[01:03:46]    |
[01:03:46] 1  | use core::f64::NAN;
[01:03:46]    |
[01:03:46]    |
[01:03:46] 1  | use std::f32::NAN;
[01:03:46]    |
[01:03:46] 1  | use std::f64::NAN;
[01:03:46]    |
[01:03:46] 
[01:03:46] error[E0425]: cannot find value `INFINITY` in this scope
[01:03:46]   --> libcore/../libcore/tests/num/int_macros.rs:50:29
[01:03:46]    |
[01:03:46] 50 |         assert!(NAN.div_euc(INFINITY).is_nan());
[01:03:46]    | 
[01:03:46]    | 
[01:03:46]   ::: libcore/../libcore/tests/num/i8.rs:11:1
[01:03:46]    |
[01:03:46] 11 | int_module!(i8, i8);
[01:03:46]    | -------------------- in this macro invocation
[01:03:46] help: possible candidates are found in other modules, you can import them into scope
[01:03:46] 1  | use core::f32::INFINITY;
[01:03:46]    |
[01:03:46] 1  | use core::f64::INFINITY;
[01:03:46]    |
[01:03:46]    |
[01:03:46] 1  | use std::f32::INFINITY;
[01:03:46]    |
[01:03:46] 1  | use std::f64::INFINITY;
[01:03:46]    |
[01:03:46] 
[01:03:46] error[E0425]: cannot find value `INFINITY` in this scope
[01:03:46]   --> libcore/../libcore/tests/num/int_macros.rs:36:17
[01:03:46]    |
[01:03:46] 36 |         assert!(INFINITY.mod_euc(a).is_nan());
[01:03:46]    | 
[01:03:46]    | 
[01:03:46]   ::: libcore/../libcore/tests/num/i16.rs:11:1
[01:03:46]    |
[01:03:46] 11 | int_module!(i16, i16);
[01:03:46]    | ---------------------- in this macro invocation
[01:03:46] help: possible candidates are found in other modules, you can import them into scope
[01:03:46] 1  | use core::f32::INFINITY;
[01:03:46]    |
[01:03:46] 1  | use core::f64::INFINITY;
[01:03:46]    |
[01:03:46]    |
[01:03:46] 1  | use std::f32::INFINITY;
[01:03:46]    |
[01:03:46] 1  | use std::f64::INFINITY;
[01:03:46]    |
[01:03:46] 
[01:03:46] error[E0425]: cannot find value `a` in this scope
[01:03:46]   --> libcore/../libcore/tests/num/int_macros.rs:36:34
[01:03:46]    |
[01:03:46] 36 |         assert!(INFINITY.mod_euc(a).is_nan());
[01:03:46]    |                                  ^ did you mean `A`?
[01:03:46]    | 
[01:03:46]   ::: libcore/../libcore/tests/num/i16.rs:11:1
[01:03:46]    |
[01:03:46] 11 | int_module!(i16, i16);
[01:03:46]    | ---------------------- in this macro invocation
[01:03:46] error[E0425]: cannot find value `a` in this scope
[01:03:46] error[E0425]: cannot find value `a` in this scope
[01:03:46]   --> libcore/../libcore/tests/num/int_macros.rs:37:20
[01:03:46]    |
[01:03:46] 37 |         assert_eq!(a.mod_euc(INFINITY), a);
[01:03:46]    |                    ^ did you mean `A`?
[01:03:46]    | 
[01:03:46]   ::: libcore/../libcore/tests/num/i16.rs:11:1
[01:03:46]    |
[01:03:46] 11 | int_module!(i16, i16);
[01:03:46]    | ---------------------- in this macro invocation
[01:03:46] 
[01:03:46] error[E0425]: cannot find value `INFINITY` in this scope
[01:03:46]   --> libcore/../libcore/tests/num/int_macros.rs:37:30
[01:03:46]    |
[01:03:46] 37 |         assert_eq!(a.mod_euc(INFINITY), a);
[01:03:46]    | 
[01:03:46]    | 
[01:03:46]   ::: libcore/../libcore/tests/num/i16.rs:11:1
[01:03:46]    |
[01:03:46] 11 | int_module!(i16, i16);
[01:03:46]    | ---------------------- in this macro invocation
[01:03:46] help: possible candidates are found in other modules, you can import them into scope
[01:03:46] 1  | use core::f32::INFINITY;
[01:03:46]    |
[01:03:46] 1  | use core::f64::INFINITY;
[01:03:46]    |
[01:03:46]    |
[01:03:46] 1  | use std::f32::INFINITY;
[01:03:46]    |
[01:03:46] 1  | use std::f64::INFINITY;
[01:03:46]    |
[01:03:46] 
[01:03:46] error[E0425]: cannot find value `a` in this scope
[01:03:46]   --> libcore/../libcore/tests/num/int_macros.rs:37:41
[01:03:46]    |
[01:03:46] 37 |         assert_eq!(a.mod_euc(INFINITY), a);
[01:03:46]    |                                         ^ did you mean `A`?
[01:03:46]    | 
[01:03:46]   ::: libcore/../libcore/tests/num/i16.rs:11:1
[01:03:46]    |
[01:03:46] 11 | int_module!(i16, i16);
---
[01:03:58] 
[01:03:58] To learn more, run the command again with --verbose.
[01:03:58] 
[01:03:58] 
[01:03:58] command did not execute successfully: "/checkout/obj/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "test" "--target" "x86_64-unknown-linux-gnu" "-j" "4" "--release" "--locked" "--color" "always" "--features" "panic-unwind jemalloc backtrace" "--manifest-path" "/checkout/src/libstd/Cargo.toml" "-p" "core" "--" "--quiet"
[01:03:58] 
[01:03:58] 
[01:03:58] failed to run: /checkout/obj/build/bootstrap/debug/bootstrap test
[01:03:58] Build completed unsuccessfully in 0:23:24
[01:03:58] Build completed unsuccessfully in 0:23:24
[01:03:58] make: *** [check] Error 1
[01:03:58] Makefile:58: recipe for target 'check' failed

The command "stamp sh -x -c "$RUN_SCRIPT"" exited with 2.
travis_time:start:085ce750
$ date && (curl -fs --head https://google.com | grep ^Date: | sed 's/Date: //g' || true)

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 @TimNN. (Feature Requests)

@alexcrichton
Copy link
Member

Looks like there may be some travis errors to help clean up?

@clamydo
Copy link
Contributor Author

clamydo commented May 25, 2018

I blindly added the tests to libcore/tests/num/int_macros.rs, but now I realize it is just the wrong spot. What is the correct location to add unit tests for methods on f32/f64?

@alexcrichton
Copy link
Member

Ah anywhere within libcore/tests should be fine

@clamydo
Copy link
Contributor Author

clamydo commented May 28, 2018

I've moved the unit tests to libcore/tests/mod.rs. When I run the tests, it works for .mod_euc() but for .div_euc() I get

no method named `div_euc` found for type `{float}` in the current scope

and I do not understand why. Sorry for the trouble.

@rust-highfive
Copy link
Collaborator

The job x86_64-gnu-llvm-3.9 of your PR failed on Travis (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.
[00:47:11] ................................................................i...................................
[00:47:15] ....................................................................................................
[00:47:20] ....................................................................................................
[00:47:27] .............................................................................................i......
[00:47:29] ...........iiiiiiiii...................................................
[00:47:29] 
[00:47:29] travis_fold:start:test_ui_nll
travis_time:start:test_ui_nll
Check compiletest suite=ui mode=ui compare_mode=nll (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
---
[00:48:16] ................................................................i...................................
[00:48:20] ....................................................................................................
[00:48:25] ....................................................................................................
[00:48:31] .............................................................................................i......
[00:48:34] ...........iiiiiiiii...................................................
[00:48:34] 
[00:48:34]  finished in 64.264
[00:48:34] travis_fold:end:test_ui_nll

---
[01:10:23] travis_fold:start:test_stage1-core
travis_time:start:test_stage1-core
Testing core stage1 (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
[01:10:23]    Compiling core v0.0.0 (file:///checkout/src/libcore)
[01:10:32] error[E0599]: no method named `div_euc` found for type `{float}` in the current scope
[01:10:32]    --> libcore/../libcore/tests/num/mod.rs:569:26
[01:10:32]     |
[01:10:32] 569 |             assert_eq!(a.div_euc($inf), 0.0);
[01:10:32] ...
[01:10:32] ...
[01:10:32] 578 | test_float!(f32, f32, ::core::f32::INFINITY, ::core::f32::NEG_INFINITY, ::core::f32::NAN);
[01:10:32]     | ------------------------------------------------------------------------------------------ in this macro invocation
[01:10:32] 
[01:10:32] error[E0599]: no method named `div_euc` found for type `{float}` in the current scope
[01:10:32]    --> libcore/../libcore/tests/num/mod.rs:570:23
[01:10:32]     |
[01:10:32] 570 |             assert!(a.div_euc($nan).is_nan());
[01:10:32] ...
[01:10:32] ...
[01:10:32] 578 | test_float!(f32, f32, ::core::f32::INFINITY, ::core::f32::NEG_INFINITY, ::core::f32::NAN);
[01:10:32]     | ------------------------------------------------------------------------------------------ in this macro invocation
[01:10:32] 
[01:10:32] error[E0599]: no method named `div_euc` found for type `{float}` in the current scope
[01:10:32]    --> libcore/../libcore/tests/num/mod.rs:569:26
[01:10:32]     |
[01:10:32] 569 |             assert_eq!(a.div_euc($inf), 0.0);
[01:10:32] ...
[01:10:32] ...
[01:10:32] 579 | test_float!(f64, f64, ::core::f64::INFINITY, ::core::f64::NEG_INFINITY, ::core::f64::NAN);
[01:10:32]     | ------------------------------------------------------------------------------------------ in this macro invocation
[01:10:32] 
[01:10:32] error[E0599]: no method named `div_euc` found for type `{float}` in the current scope
[01:10:32]    --> libcore/../libcore/tests/num/mod.rs:570:23
[01:10:32]     |
[01:10:32] 570 |             assert!(a.div_euc($nan).is_nan());
[01:10:32] ...
[01:10:32] ...
[01:10:32] 579 | test_float!(f64, f64, ::core::f64::INFINITY, ::core::f64::NEG_INFINITY, ::core::f64::NAN);
[01:10:32]     | ------------------------------------------------------------------------------------------ in this macro invocation
[01:10:37] error: aborting due to 4 previous errors
[01:10:37] 
[01:10:37] For more information about this error, try `rustc --explain E0599`.
[01:10:37] error: Could not compile `core`.
[01:10:37] error: Could not compile `core`.
[01:10:37] 
[01:10:37] To learn more, run the command again with --verbose.
[01:10:37] 
[01:10:37] 
[01:10:37] command did not execute successfully: "/checkout/obj/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "test" "--target" "x86_64-unknown-linux-gnu" "-j" "4" "--release" "--locked" "--color" "always" "--features" "panic-unwind jemalloc backtrace" "--manifest-path" "/checkout/src/libstd/Cargo.toml" "-p" "core" "--" "--quiet"
[01:10:37] 
[01:10:37] 
[01:10:37] failed to run: /checkout/obj/build/bootstrap/debug/bootstrap test
[01:10:37] Build completed unsuccessfully in 0:25:41
[01:10:37] Build completed unsuccessfully in 0:25:41
[01:10:37] make: *** [check] Error 1
[01:10:37] Makefile:58: recipe for target 'check' failed

The command "stamp sh -x -c "$RUN_SCRIPT"" exited with 2.
travis_time:start:0648d40a
$ date && (curl -fs --head https://google.com | grep ^Date: | sed 's/Date: //g' || true)

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 @TimNN. (Feature Requests)

@shepmaster
Copy link
Member

and I do not understand why.

A floating point literal like 42.0 doesn't have a fixed type yet; it can be f32 or f64 depending on context.

The difference is that the first test uses a known-type value (e.g. INFINITY) first. Since there's only the one implementation of the method when the receiver type is known, type inference can pin down the type of a. However, the second test uses the literal first, but there's multiple implementations for the method (depending on if it's f32 or f64):

#![feature(euclidean_division)]

use std::f32::INFINITY;

#[test]
fn mod_euc() {
    let a = 42.0;
    assert!(INFINITY.mod_euc(a).is_nan());
}

#[test]
fn div_euc() {
    let a = 42.0;
    assert_eq!(a.div_euc(INFINITY), 0.0);
}

TL;DR: pin down the type:

let a: $fty = 42.0;

@clamydo
Copy link
Contributor Author

clamydo commented Jun 7, 2018

Thanks, that solved the issue. Good to go.

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

The reviewer is away, maybe someone else from @rust-lang/libs can review this?

1 similar comment
@pietroalbini
Copy link
Member

The reviewer is away, maybe someone else from @rust-lang/libs can review this?

@Mark-Simulacrum
Copy link
Member

r? @BurntSushi

@BurntSushi
Copy link
Member

The diff LGTM and it looks like other folks who know more about this topic than I do are happy as well, so @bors r+

(@Mark-Simulacrum FYI, I removed myself from the rust-highfive rotation because my time is so constrained. Happy to do the occasional review though!)

@bors
Copy link
Contributor

bors commented Jun 25, 2018

📌 Commit b5847bf37c2235fd10af46009a614423de214e39 has been approved by BurntSushi

@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 Jun 25, 2018
@bors
Copy link
Contributor

bors commented Jun 25, 2018

🔒 Merge conflict

This pull request and the master branch diverged in a way that cannot be automatically merged. Please rebase on top of the latest master branch, and let the reviewer approve again.

How do I rebase?

Assuming self is your fork and upstream is this repository, you can resolve the conflict following these steps:

  1. git checkout euclidean (switch to your branch)
  2. git fetch upstream master (retrieve the latest master)
  3. git rebase upstream/master -p (rebase on top of it)
  4. Follow the on-screen instruction to resolve conflicts (check git status if you got lost).
  5. git push self euclidean --force-with-lease (update this PR)

You may also read Git Rebasing to Resolve Conflicts by Drew Blessing for a short tutorial.

Please avoid the "Resolve conflicts" button on GitHub. It uses git merge instead of git rebase which makes the PR commit history more difficult to read.

Sometimes step 4 will complete without asking for resolution. This is usually due to difference between how Cargo.lock conflict is handled during merge and rebase. This is normal, and you should still perform step 5 to update this PR.

Error message
Auto-merging src/libstd/f64.rs
Auto-merging src/libstd/f32.rs
Auto-merging src/libcore/tests/num/mod.rs
CONFLICT (file/directory): There is a directory with name src/libbacktrace in heads/homu-tmp. Adding src/libbacktrace as src/libbacktrace~HEAD
Automatic merge failed; fix conflicts and then commit the result.

@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 Jun 25, 2018
@pietroalbini
Copy link
Member

@fkjogu you need to rebase on the latest master, even if you don't see any conflict (sorry about that!)

@clamydo
Copy link
Contributor Author

clamydo commented Jun 27, 2018

I've rebased (a few hours ago). How can I check, if I need to rebase again?

@pietroalbini
Copy link
Member

That should be enough, thanks!

@bors r=BurntSushi rollup

@bors
Copy link
Contributor

bors commented Jun 27, 2018

📌 Commit bd853a6 has been approved by BurntSushi

@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-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Jun 27, 2018
kennytm added a commit to kennytm/rust that referenced this pull request Jun 27, 2018
Document round-off error in `.mod_euc()`-method, see issue rust-lang#50179

Due to a round-off error the method `.mod_euc()` of both `f32` and `f64` can produce mathematical invalid outputs. If `self` in magnitude is much small than the modulus `rhs` and negative, `self + rhs` in the first branch cannot be represented in the given precision and results into `rhs`. In the mathematical strict sense, this breaks the definition. But given the limitation of floating point arithmetic it can be thought of the closest representable value to the true result, although it is not strictly in the domain `[0.0, rhs)` of the function. It is rather the left side asymptotical limit. It would be desirable that it produces the mathematical more sound approximation of `0.0`, the right side asymptotical limit. But this breaks the property, that `self == self.div_euc(rhs) * rhs + a.mod_euc(rhs)`.

The discussion in issue rust-lang#50179 did not find an satisfying conclusion to which property is deemed more important. But at least we can document the behaviour. Which this pull request does.
bors added a commit that referenced this pull request Jun 27, 2018
Rollup of 7 pull requests

Successful merges:

 - #49987 (Add str::split_ascii_whitespace.)
 - #50342 (Document round-off error in `.mod_euc()`-method, see issue #50179)
 - #51658 (Only do sanity check with debug assertions on)
 - #51799 (Lower case some feature gate error messages)
 - #51800 (Add a compiletest header for edition)
 - #51824 (Fix the error reference for LocalKey::try_with)
 - #51842 (Document that Layout::from_size_align does not allow align=0)

Failed merges:

r? @ghost
@bors bors merged commit bd853a6 into rust-lang:master Jun 28, 2018
@clamydo clamydo deleted the euclidean branch July 20, 2018 08:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.