Julia: `real(Number) -> Int`

Created on 28 May 2018  路  6Comments  路  Source: JuliaLang/julia

I would have expected real(Number) -> Real, but instead I got real(Number) -> Int. One can argue whether real(Number) -> Real is truly the right definition, but clearly real(Number) -> Int makes no sense.

The problem is caused by the following three lines:

https://github.com/JuliaLang/julia/blob/cfca311622fb25a0c80642d675d73de3a49fe875/base/complex.jl#L108-L110

Most helpful comment

I agree that either returning Real or throwing seem better than the current definition. In general, calling real on an abstract type is suspicious. The docstring says: "Return the type that represents the real part of a value of type T". For an abstract type, there is no way of knowing. After all, one could define a new subtype of T any moment. In this particular case, Real might be a reasonable upper bound, but surely, there are valid exceptions. It's a bit like the dreaded promote_op. (BTW, Base.promote_op(real, Number) gives Number.)

That said, your convert definition likely should convert(T, ...) at the end to be sure to return a T , thereby circumventing the problem in this particular case. And, of course, convert(Number, ::LogNumber) should be a no-op.

All 6 comments

Can you provide a code snippet of exactly what you are tried, and what you got?

julia> real(Number)
Int64

fredrikekre provided the obvious answer, but I assume you were asking in what context I discovered this issue. I am currently working on a LogNumber type that stores a floating point number as sign times logarithm of absolute value to avoid over-/underflow, i.e.

type LogNumber{S,L} <: Number
    sign::S
    logabs::L
end

Base.convert(::Type{<:LogNumber}, x::LogNumber) = x
Base.convert(::Type{T}, x::LogNumber) where {T <: Number} = convert(T,x.sign) * exp(convert(real(T), x.logabs))

I then called broadcast(f,v) with arguments such that it should have returned Vector{LogNumber}, but somehow broadcast allocated a Vector{Number} instead. Calling setindex! on this vector called convert(::Type{Number}, ::LogNumber} which in turn called convert(real(Number), x.logabs) == convert(Int, x.logabs) which resulted in an InexactError().

Unfortunately, I did not manage to construct a MWE for the broadcast call, and in any case there are things which are at least dubious in the above code, but the point remains that real(Number) -> Int seems wrong, and debugging would have been easier for me if either real(Number) -> Real or real(Number) -> Error().

I agree that either returning Real or throwing seem better than the current definition. In general, calling real on an abstract type is suspicious. The docstring says: "Return the type that represents the real part of a value of type T". For an abstract type, there is no way of knowing. After all, one could define a new subtype of T any moment. In this particular case, Real might be a reasonable upper bound, but surely, there are valid exceptions. It's a bit like the dreaded promote_op. (BTW, Base.promote_op(real, Number) gives Number.)

That said, your convert definition likely should convert(T, ...) at the end to be sure to return a T , thereby circumventing the problem in this particular case. And, of course, convert(Number, ::LogNumber) should be a no-op.

It would be nice to remove zero(Number) === 0 (and one(Number), and similar for other abstract types). That's what's causing this. I wonder how disruptive that would be.

@JeffBezanson, the difficulty is that this may cause some code to fail if inference fails, rather than just to be slow. e.g. will sum(Number[]) fail if you remove the zero method?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

dpsanders picture dpsanders  路  3Comments

tkoolen picture tkoolen  路  3Comments

sbromberger picture sbromberger  路  3Comments

omus picture omus  路  3Comments

wilburtownsend picture wilburtownsend  路  3Comments