Skip to content

Fixed an issue with NumericRanges until/to Long.MinValue's length #10328

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

Closed

Conversation

AminMal
Copy link
Contributor

@AminMal AminMal commented Feb 26, 2023

Fixes an issue with the evaluation of length on NumericRanges going onto Long.MinValue.

Fixes scala/bug#12716

@scala-jenkins scala-jenkins added this to the 2.13.11 milestone Feb 26, 2023
@@ -402,7 +402,7 @@ object NumericRange {
val startside = num.sign(start)
val endside = num.sign(end)
num.toInt{
if (num.gteq(num.times(startside, endside), zero)) {
if (num.gt(num.times(startside, endside), zero)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

[0, Long.MinValue] by any negative step is handled here, while it's definitely not safe to subtract 0 with MinValue.

Copy link
Contributor

Choose a reason for hiding this comment

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

The condition is now a very slow way to write startside == endside. So one could use that instead. But the reason to include zero is to make the most usual range, which is [0, n), to be easy to calculate.

Can we just drop startside and endside entirely and instead compute num.lt(start, zero) == num.lt(end, zero) (i.e. either both negative or both non-negative)? This will only admit Int.MinValue when the range is no bigger than -1 to Int.MinValue, which is okay to subtract.

@SethTisue SethTisue added the library:collections PRs involving changes to the standard collection library label Mar 13, 2023
@SethTisue
Copy link
Member

let's see if we can interest @Ichoran in scrutinizing this one too...

@SethTisue SethTisue modified the milestones: 2.13.11, 2.13.12 Mar 13, 2023
@SethTisue SethTisue requested a review from a team March 13, 2023 21:41
Copy link
Contributor

@Ichoran Ichoran left a comment

Choose a reason for hiding this comment

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

I am I'm not entirely sure that this is correct, but I haven't tried very hard to check because of a different issue. The change makes the comments no longer apply: you don't necessarily have three pieces, or if you do, they're not the pieces it says. In order to change this code, it's very helpful to have comments that reflect what is going on. Furthermore, the comments that were added assume Long, but we don't know that it's a Long. We only know that it doesn't fit in Int.

So you could carefully update the documentation, explaining the different possible states.

But I recommend adding a third case, instead. The first case stays the same (but use equal signs as the test, which is equivalent to but faster than the multiplication and test against zero). The second, one-endpoint-is-zero case does not compute the difference at all, instead first it divides the nonzero value by step and then subtracts if the nonzero value was the start; then you can check the sign and if it's wrong, it was going to be out of bounds anyway so just throw an exception. If it's okay, use the same logic as in the both-same-sign case. The third case stays at it is, with the documentation matching that case.

Anyway, either way I think some changes are in order.

@lrytz lrytz modified the milestones: 2.13.12, 2.13.13 Jul 5, 2023
@SethTisue SethTisue marked this pull request as draft August 8, 2023 22:27
@SethTisue
Copy link
Member

@AminMal interested in returning to this?

@AminMal
Copy link
Contributor Author

AminMal commented Sep 23, 2023

@AminMal interested in returning to this?

@SethTisue Yeah for sure, I'll make sure to improve the comments and update the PR accordingly.

@lrytz lrytz modified the milestones: 2.13.13, 2.13.14 Nov 28, 2023
@AminMal
Copy link
Contributor Author

AminMal commented Dec 2, 2023

@Ichoran Sorry for the late response, and thanks for the review!
I think I understand what you're saying, but I'm not sure if that's the case. This PR basically adds another layer of checking within the 3rd "piece" of the process. The thing is, num.quot("num.MinValue", "-1 or 1) basically returns the MinValue, and the check method isn't supposed to be called with negative numbers. So basically it gets bypassed (check(num.quot(enddiff, step)))!
I can think of two other ways to do this:

  • To modify the check method to check for negative values as well. Since it's supposed to be called only with positive numbers. But it's not a preference of mine (maybe renaming it would be better).
  • Since I guess the root problem lies within Integral::quot, which doesn't throw exceptions when it's called by (MinValue, +-1), making modifications to it's implementations would do the trick. But its downside is that it probably causes side effects all over the place.

Right now I'm coming up with an update to this PR which makes it "more readable", it'd be great if you could take a look at it again, to see if you agree with the new documentations on it. Also if you think any of the alternatives is a better way to solve this issue, please let me know.

@AminMal
Copy link
Contributor Author

AminMal commented Dec 6, 2023

@SethTisue Is it okay for me to mark this PR as "ready to review"?

@lrytz
Copy link
Member

lrytz commented Dec 6, 2023

@AminMal absolutely, go ahead!

@AminMal AminMal marked this pull request as ready for review December 6, 2023 22:57
@AminMal AminMal requested a review from Ichoran December 23, 2023 15:53
Copy link
Contributor

@Ichoran Ichoran left a comment

Choose a reason for hiding this comment

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

Seems to solve the problem, but I think it requires more computation than it should.

@@ -402,7 +402,7 @@ object NumericRange {
val startside = num.sign(start)
val endside = num.sign(end)
num.toInt{
if (num.gteq(num.times(startside, endside), zero)) {
if (num.gt(num.times(startside, endside), zero)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

The condition is now a very slow way to write startside == endside. So one could use that instead. But the reason to include zero is to make the most usual range, which is [0, n), to be easy to calculate.

Can we just drop startside and endside entirely and instead compute num.lt(start, zero) == num.lt(end, zero) (i.e. either both negative or both non-negative)? This will only admit Int.MinValue when the range is no bigger than -1 to Int.MinValue, which is okay to subtract.

@SethTisue SethTisue marked this pull request as draft January 18, 2024 03:39
@SethTisue SethTisue modified the milestones: 2.13.14, 2.13.15 Mar 13, 2024
@SethTisue
Copy link
Member

@AminMal interested in returning to this...?

@SethTisue SethTisue modified the milestones: 2.13.15, 2.13.16 Aug 14, 2024
@SethTisue SethTisue removed this from the 2.13.16 milestone Nov 6, 2024
@SethTisue
Copy link
Member

closing for inactivity — we can reopen it if @AminMal reappears, or a volunteer could submit a new PR based on this one

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
library:collections PRs involving changes to the standard collection library
Projects
None yet
Development

Successfully merging this pull request may close these issues.

NumericRange.length fails for wideLong ranges.
5 participants