Julia: iszero vs x==0 and countnz, find etc.

Created on 28 Jul 2017  ·  12Comments  ·  Source: JuliaLang/julia

In short

julia> "Julia" == 0
false

but

julia> iszero("Julia")
ERROR: MethodError: no method matching zero(::String)
Closest candidates are:
  zero(::Type{Base.LibGit2.GitHash}) at libgit2/oid.jl:106
  zero(::Type{Base.Pkg.Resolve.VersionWeights.VWPreBuildItem}) at pkg/resolve/versionweight.jl:82
  zero(::Type{Base.Pkg.Resolve.VersionWeights.VWPreBuild}) at pkg/resolve/versionweight.jl:124
  ...
Stacktrace:
 [1] iszero(::String) at ./number.jl:22

But String is relatively arbitrarily chosen here. An implication of this is that

julia> countnz([:Julia])
1

julia> countnz([[0.0]])
1

julia> !iszero([0.0])
false

because countnz tests with x != 0. It could also test with !iszero but then countnz([:Julia]) would fail.

This came up in https://github.com/JuliaLang/julia/pull/22945 but is kind of separate from the PR so I think it deserves its own issue. There was a previous discussion of this issue in https://github.com/JuliaLang/julia/pull/17623#discussion_r92877227 but I'm not sure how many people noticed.

Possible solutions I can come up with are

  1. Define fallback iszero(x) = false and use !iszero whenever testing for zero in e.g. countnz and find. This would make countnz([[0.0]]) == 0 and countnz([:Julia]) == 1
  2. Remove the fallback == method or make it try to convert the arguments to the same type and use !iszero in countnz and find. This would make countnz([[0.0]]) == 0 and countnz([:julia]) fail, but probably also make a million other things throw.
  3. Leave iszero and == as they are but use !iszero in countnz and find. This would make countnz([[0.0]]) == 0 and countnz([:julia]) fail.
  4. Don't do anything. This would make countnz([[0.0]]) == 1 (which I think is the wrong result) and countnz([:Julia]) == 1

Update: I think we should add this to the milestone for 1.0.

Most helpful comment

Removing countnz entirely is ok with me too. I like deleting stuff :)

All 12 comments

I've updated the top post with possible solutions

I guess the root of the problem is that x == 0 is ambiguous: it could be asking whether x is very much like the object 0, or it could be asking whether x is the zero element of its group. For == I think we have no choice but to pick the first interpretation. So I guess I favor option (3): make all functions that explicitly name "zero" or "z" use the group-element interpretation, and leave == alone.

I think == 0 is a better fallback than false (maybe only tried when zero is not applicable?) but otherwise 1 seems like the more desirable behavior to me - if there is no zero element, the answer is no, why error? I anticipate people would immediately start defining zero(::String) in response to the error they would get here, and I don't think we should encourage that

Or perhaps iszero(x) = (x == zero(x))? I'm fine with getting an error from countnz(["str"]).

isn't that what it is now?

+1 to the idea of making countnz call iszero though.

I've only just started reading the issue, but the status quo actual seems best to me.
I think the iszero usage can be easily written:

count(iszero, [1, 2, 3, 0])

Along the lines of what @vtjnash is proposing, I propose getting rid of the countnz function entirely in favor of count(!iszero, A) or whatever your favorite predicate is.

I think you mean count(!iszero, [1, 2, 3, 0]), right?

Removing countnz entirely is ok with me too. I like deleting stuff :)

Efficient implementation for sparse is roughly nnz(S)*pred(default) + count(p, S.values).

Fixed by #23485.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

i-apellaniz picture i-apellaniz  ·  3Comments

ararslan picture ararslan  ·  3Comments

wilburtownsend picture wilburtownsend  ·  3Comments

TotalVerb picture TotalVerb  ·  3Comments

StefanKarpinski picture StefanKarpinski  ·  3Comments