-
-
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
linspace: try to "lift" linspace the float ranges are [close #9637] #9666
Conversation
Is there any way to have |
Not quite – the lifted cases are the same, but the unlifted cases are different: ranges use |
0c19ae2
to
480f552
Compare
aa1660a
to
1ef8a7e
Compare
2b5ec5e
to
0e13a1e
Compare
This now seems to be uniformly better than what we had before – the tests are pretty comprehensive. There are still some corner cases like this: julia> linspace(realmin(),realmax(),maxintfloat())
linspace(0.0,1.7976931348623157e308,9007199254740992) Note that the start of the resulting |
looks like there's a 32 bit failure |
aa30ada
to
03c2607
Compare
The length zero and one cases for LinSpace are really weird – arguably they simply shouldn't be allowed at all. However, I think for usability we really need to support these. This change uses -1 and 0 as special values of the r.len field, encoding, somewhat counterintuitively, real lengths of 0 and 1, respectively. The reason for this is so that the element access computations for the length one case has r.len-1 == -1 and only flips sign, avoiding overflow when r.start is very large. In the length zero case, r.len-1 == -2, so we need to divide by -2 to get correct first and last values back. This is susceptible to overflow, but since these values are kind of nonsense in the zero case, this seems to be a preferrable situation. There are still cases where the (r.len-i)*r.start + (i-1)*r.stop can overflow for longer ranges, and it's unclear to me whether you can handle those correctly with the same LinSpace type. This plus the complexity of handling corner cases like lengths zero and one, makes me wonder if it's really a good idea to make this a type at all.
This forces the LinSpace divisor – either n*e or just n – to be in the range [1/2,0), which, in particular, prevents overflow even if the old computation could overflow. Needs more testing of corner cases, but this passes all of the existing tests at least.
E.g. linspace(realmin(), realmax(), maxintfloat())
The issue was that doing `one(p) << p` where p is Int produces zero on a 32-bit system if p is larger than 32, which it can be for some Float64 arguments. Getting tests to pass for 32-bit also required avoiding taking the length of LinSpace objects whose lengths are 2^31 or longer. This is a bit of a more general problem since we can represent FloatRange and LinSpace objects whose length cannot be represented as an Int on both 64- and 32-bit platforms, although it is, of course, easier to encounter the problem on 32-bit systems. In fact, on 64-bit systems it is not actually possible to construct LinSpace{Float64} objects whose length cannot be represented as an Int. The issue does exist for FloatRange on 64-bit systems, however. One possible solution is to introduce length(T,x) that gives lengths in a particular type and just define length(x) as length(Int,x) as the default behavior.
03c2607
to
aa78037
Compare
linspace: try to "lift" linspace the float ranges are [close #9637]
What should the policy be for these? julia> [0.0,0.5,1.0] == linspace(0.0, 1.0, 3)
false
julia> convert(Vector, linspace(0.0, 1.0, 3))
ERROR: MethodError: `convert` has no method matching convert(::Type{Array{T,1}}, ::LinSpace{Float64})
This may have arisen from a call to the constructor Array{T,1}(...),
since type constructors fall back to convert methods.
Closest candidates are:
convert{T}(::Type{T}, ::T)
convert{T<:Integer}(::Type{T<:Integer}, ::Rational{T<:Integer})
convert{T<:Integer}(::Type{T<:Integer}, ::Float16)
... |
Bump @StefanKarpinski |
So we have the same issue with FloatRanges: julia> [0.0,0.5,1.0] == 0:0.5:1
false
julia> convert(Vector, 0:0.5:1)
ERROR: MethodError: `convert` has no method matching convert(::Type{Array{T,1}}, ::FloatRange{Float64})
This may have arisen from a call to the constructor Array{T,1}(...),
since type constructors fall back to convert methods.
Closest candidates are:
convert{T}(::Type{T}, ::T)
convert{T<:Tuple{Any,Vararg{Any}}}(::Type{T<:Tuple{Any,Vararg{Any}}}, ::Tuple{Any,Vararg{Any}})
convert{T<:Integer}(::Type{T<:Integer}, ::Rational{T<:Integer})
... |
Which doesn't really answer the question, but at least it's consistent. The same answer should apply. |
|
Shouldn't this have become a new version of linrange (and LinRange) instead of linspace (and LinSpace) since it returns a range object. Then linspace would be removed? |
Linspace is the traditional name for this kind of functionality. |
OK, I'll take your word for it. I've never run across the name in many years using Fortran and IDL and a bit of Mathematica and R. Julia was the first place I saw it. |
It's from Matlab |
I'd be fine with renaming it to linrange, but I'd like some input from others before doing that. Any opinions? |
Since you made it return a range-like object, it makes sense to me to rename it to |
So now |
An obvious solution is to do |
A call to
|
I'd just change it to be an (This is an impressive tour d'packages that you've embarked upon! Very nice work. 👍 ) |
Note that if we have automatic conversion of default arguments to the type of the argument then this wouldn't be a problem at all since the |
You've seen a lot more than I have, but it seems odd to have conversion for some arguments and not others. The type magic is already pretty daunting as it is. |
There's no magic, it would just mean that |
Ah, not for callers, just for the guy who writes the default value into his function definition. |
I think it makes a lot of sense as well @StefanKarpinski; particularly as we've made the push to have |
No description provided.