Problem with sign(pi)

The following behavior seems strange:

sign(pi)
ERROR: InexactError: Bool(-1)
Stacktrace:
 [1] Bool at .\float.jl:73 [inlined]
 [2] convert at .\number.jl:7 [inlined]
 [3] oftype(::Bool, ::Int64) at .\essentials.jl:367
 [4] sign(::Irrational{:π}) at .\number.jl:118
 [5] top-level scope at none:1

I’m I doing something wrong?

No, this seems like a bug. The problematic definition is:

sign(x::Real) = ifelse(x < zero(x), oftype(one(x),-1), ifelse(x > zero(x), one(x), typeof(one(x))(x)))

But we have:

julia> one(pi)
true

so the sign function is trying to convert -1 to a Bool. I’m not sure why one(pi) is a bool anyway, but it appears to be intentional.

I would suggest filing an issue at Issues · JuliaLang/julia · GitHub

5 Likes

I assume it’s so that calculations including pi do not promote results more than required. Bool is the “smallest” number type. This is also why I(2) turns into a matrix of bools if you call Matrix on it.

1 Like

That makes sense. In that case, one(pi) seems fine but sign() should probably have an extra method for ::AbstractIrrational to handle the fact that one(x) will not be large enough to hold -1.

1 Like

this Bool matrix also looks awkward… maybe we should just use Int8 to be the “smallest” number type. I think Bool is stored with 1 byte anyways at the end of the day?

julia> sizeof(Int8(-1))
1

julia> sizeof(true)
1

It’s not about the storage true requires to store, it’s mostly because usually promote(::Bool, ::T)::T, i.e. Bool is kind of the “neutral type” under promotion.

5 Likes

https://github.com/JuliaLang/julia/blob/ff20ff072b1141ed0504956d15718a7e0176a218/base/irrationals.jl#L151-L154

based on L154, maybe we should make the previous two 1.0 instead. Or at least 1. Breaking :frowning:

nvm, bad idea, since zero goes to false, this probably can’t be touched.


maybe this in the sign(x::Real) definition, instead:

julia> -one(pi)
-1

julia> oftype(one(pi),-1)
ERROR: InexactError: Bool(-1)

The solution here is probably to use one(signed(x)) instead of one(x) if x::Unsigned.

1 Like

The idea behind AbstractIrrational is that the irrational constants can take on the type of the other numbers used in a calculation, so e.g. pi + 1.0 gives a Float64, pi + big"1.0" a BigFloat, pi + 1f0 a Float32, etc. Returning a Float64 in one(::AbstractIrrational) would defeat this, because it would convert any Float32 you add it to (or do any other arithmetic operation with) to a Float64 instead.

4 Likes