-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Inconsistent behavior for min
, findmin
, and indmin
wrt negative zero
#14621
Comments
Details at Floating-point zero
Hope those above will help you. |
@zhmz90 Thanks for your comment, but this does not answer my question. I know the actual implementation, as I actually link to the relevant files and lines in my original post. I am more curious why |
This seems to be a side effect of the implementation for fixing #10729. I don't think functions should ever deliberately distinguish between |
I agree with @timholy on this. I think if we are to change this we would have to think hard about this. As far as I know, the only real case where signed zeros should give "distinguishable" results is for things like |
Arguably, this should reflect whatever isles does, which is currently to distinguish -0.0 and 0.0, but then again by that criterion if there is a NaN it should be the maximum of an array, which is weird as well. |
When comparing to other platforms, not a whole lot of consistency. For example,
@StefanKarpinski Regarding Admittedly, a bit awkward that However, I can imagine making a distinction patches imperfect machine arithmetic, where |
I think we should file a bug against IEEE 754 and get rid of signed zeros 😄. |
And NaN while we're at it. |
@StefanKarpinski What does @kgryte Your examples seem to be referring to 2 different functions: a) The result of For (a), I've found:
No languages seem to treat zeros differently for (b). From my understanding of it, the general idea in the IEEE754 spec is that julia> 1.0/0.0
Inf
julia> 1.0/-0.0
-Inf
julia> atan2(0.0,-1.0)
3.141592653589793
julia> atan2(-0.0,-1.0)
-3.141592653589793 For a more detailed justification, see "Branch Cuts for Complex Elementary Functions, or Much Ado About Nothing's Sign Bit", by William Kahan. |
Agreed, there's no problem here. |
@simonbyrne Thanks for link. Yes, my examples interleave |
@simonbyrne And actually, your example is also illustrative. Here is a contrived example: julia> atan2(min(0.0, -0.0), -1.0)
-3.141592653589793
julia> atan2(min(-0.0, 0.0), -1.0)
-3.141592653589793 but then julia> a = [0.0,-0.0]
julia> atan2(a[indmin(a)], -1.0)
3.141592653589793
julia> b = [-0.0,0.0]
julia> atan2(b[indmin(b)], -1.0)
-3.141592653589793 Just by switching the order of |
I was originally somewhat skeptical of the changes to On the other hand, this would be a breaking change, and give different results than any other language. Thus there is a significant onus to establish why it would be worthwhile, and so far I don't think those examples are very convincing (if the array has values on both sides of the branch cut, why would you take the minimum?). One possible option might be to provide the comparison function as an argument, similar to |
@simonbyrne, |
A suggestion I haven't seen in this thread (sorry for barging in on the discussion - stumbled on this while looking for something else and found it interesting...) is to change
with the motivation that |
A possibly non-obvious implication of that is that you would need to use a stable sort for floating-point numbers – or simulate doing so for the zeros at least, which is tricky since we don't know where the zeros need to end up until after the sorting has been done. I don't know how to do this efficiently without just using a stable sort, which is far less efficient than using an unstable sort like quicksort. |
I remember discussing this in the past, you should be able to find the issue in GitHub. |
As we're unlikely to change behaviour here, can we close this? |
I'd like to have the logic here fully spelled out in a clear way. |
How about: signed zeros are equivalent, and should give equivalent results, unless the function either |
There are three functions to compare numbers, But what about A different bag are SIMD vectors. There, the operators |
@simonbyrne, that's a pretty good start but what about |
I'm going to propose this modification to @simonbyrne's proposal for rules: Functions should obey the identity that
Note that this leaves room for functions like julia> min(-0.0, 0.0)
-0.0
julia> min(0.0, -0.0)
-0.0
julia> max(-0.0, 0.0)
0.0
julia> max(0.0, -0.0)
0.0 Or to otherwise return the more appropriate of the two signed zeros: julia> sign(0.0)
0.0
julia> sign(-0.0)
-0.0 This does not leave room for julia> indmin([2.0, 1.0, 0.0, -0.0])
3
julia> indmin([2.0, 1.0, -0.0, 0.0])
3
julia> findmin([2.0, 1.0, 0.0, -0.0])
(0.0,3)
julia> findmin([2.0, 1.0, -0.0, 0.0])
(-0.0,3) Since both julia> minimum([2.0, 1.0, 0.0, -0.0])
-0.0
julia> minimum([2.0, 1.0, -0.0, 0.0])
-0.0
julia> minimum([2.0, 1.0, 0.0, 0.0])
0.0 |
I'm adding the documentation label here. Where's an appropriate place to put this in the docs? |
Interesting point here – the documentation for help?> findmin
search: findmin findmin! findmax findmax!
findmin(A, dims) -> (minval, index)
For an array input, returns the value and index of the minimum over the
given dimensions.
findmin(itr) -> (x, index)
Returns the minimum element and its index.
help?> indmin
search: indmin findmin findmin!
indmin(itr) -> Integer
Returns the index of the minimum element in a collection. Of course, that violates the above rules, but maybe that's an argument for different rules? |
+1 for the rules above. As for where to document this, maybe it's the occasion to start a section in the manual about conventions one should respect when programming in Julia, which could also be used to document the expectations of standard interfaces? It could go right after the style guide. I would think |
As long as behavior is clearly documented and the design decision is clear, the proposed rules sound reasonable. Just to circle back to my original concern regarding consistency and the reason why julia> a = [2.0, 1.0, 0.0, -0.0]
julia> minimum(a)
-0.0
julia> a[indmin(a)]
0.0 return different results. The main gist is that In terms of documentation, might be beneficial to actually list the operations where the sign of The fact that various numeric computing environments seem to implement the same functionality differently suggests that no "rules" exist, beside the bare minimum set forth by IEEE 754, detailing when the sign of |
I have to say it's tempting to have |
@StefanKarpinski It's expensive, since one may have to generate multiple machine instructions for this. LLVM's intrinsic |
We're already returning |
Fixed by #23155 |
The behavior of
min
accommodates negative zero such thatwhile
indmin
, which usesfindmin
, does not accommodate negative zeroWhat is the reasoning behind this discrepancy? I would expect the behavior to be consistent, but possibly there exists a rationale for when to consider and not consider the distinction between positive and negative zero in core functions.
The text was updated successfully, but these errors were encountered: