Skip to content
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

RFC: More methods for Irrational #32117

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 33 additions & 5 deletions base/irrationals.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ abstract type AbstractIrrational <: Real end

Number type representing an exact irrational value denoted by the
symbol `sym`.

Note: `Irrational`s are converted to `Float64` before use by most
built-in mathematical operators. Conversion to high-precision types
such as `BigFloat` should be done before the first operation,
as in `2*big(π)` rather than `big(2π)`. The latter would only be
accurate to about 16 decimal places.
"""
struct Irrational{sym} <: AbstractIrrational end

Expand All @@ -45,6 +51,7 @@ promote_rule(::Type{S}, ::Type{T}) where {S<:AbstractIrrational,T<:Number} = pro
AbstractFloat(x::AbstractIrrational) = Float64(x)::Float64
Float16(x::AbstractIrrational) = Float16(Float32(x)::Float32)
Complex{T}(x::AbstractIrrational) where {T<:Real} = Complex{T}(T(x))
Complex(x::AbstractIrrational, y::AbstractIrrational) = Complex(Float64(x), Float64(y))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems unnecessary?

julia> Complex(π, ℯ)
3.141592653589793 + 2.718281828459045im
Suggested change
Complex(x::AbstractIrrational, y::AbstractIrrational) = Complex(Float64(x), Float64(y))


@pure function Rational{T}(x::AbstractIrrational) where T<:Integer
o = precision(BigFloat)
Expand Down Expand Up @@ -151,14 +158,38 @@ zero(::Type{<:AbstractIrrational}) = false
one(::AbstractIrrational) = true
one(::Type{<:AbstractIrrational}) = true

oneunit(T::Type{<:AbstractIrrational}) = throw(ArgumentError("The number one cannot be of type $T"))
oneunit(x::T) where {T <: AbstractIrrational} = oneunit(T)

-(x::AbstractIrrational) = -Float64(x)
for op in Symbol[:+, :-, :*, :/, :^]
for op in Symbol[:+, :-, :*, :/, :^, :cld, :div, :divrem, :fld, :mod, :mod1, :rem, :UnitRange]
@eval $op(x::AbstractIrrational, y::AbstractIrrational) = $op(Float64(x),Float64(y))
end
*(x::Bool, y::AbstractIrrational) = ifelse(x, Float64(y), 0.0)
*(x::Bool, y::AbstractIrrational) = x ? y : zero(y)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type unstable --- I'm not sure this is worth it.

*(x::AbstractIrrational, y::Bool) = y * x
+(x::Bool, y::AbstractIrrational) = x ? 1.0 + y : y
+(x::AbstractIrrational, y::Bool) = y + x
-(x::AbstractIrrational, y::Bool) = y ? x - 1.0 : x

round(x::Irrational, r::RoundingMode) = round(float(x), r)

Rational(x::AbstractIrrational) = rationalize(x)
rationalize(x::Irrational{T}) where T = throw(ArgumentError("Type must be specified. Use rationalize(Int, $T) to obtain a Rational{Int} approximation to the irrational constant $T."))
@generated Math.mod2pi(x::AbstractIrrational) = Float64(mod2pi(big(x())))
@generated Math.rem2pi(x::AbstractIrrational, r::RoundingMode) = Float64(rem2pi(big(x()), r()))
Comment on lines +178 to +179
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@generated Math.mod2pi(x::AbstractIrrational) = Float64(mod2pi(big(x())))
@generated Math.rem2pi(x::AbstractIrrational, r::RoundingMode) = Float64(rem2pi(big(x()), r()))
Math.mod2pi(x::AbstractIrrational) = Float64(mod2pi(big(x)))
Math.rem2pi(x::AbstractIrrational, r::RoundingMode) = Float64(rem2pi(big(x), r))


# sin(float(pi)) is computed from the difference between accurate pi and float(pi),
# but sin(pi) should return zero. (The same applies to tan(pi).)
sin(::Irrational{:π}) = 0.0
tan(::Irrational{:π}) = 0.0
Math.sincos(::Irrational{:π}) = (0.0, -1.0)

sign(x::AbstractIrrational) = sign(Float64(x))

widemul(x::AbstractIrrational, y::AbstractIrrational) = big(x)*big(y)

(::Colon)(x::AbstractIrrational, y::AbstractIrrational) = Float64(x):Float64(y)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to already work without this?

Suggested change
(::Colon)(x::AbstractIrrational, y::AbstractIrrational) = Float64(x):Float64(y)
julia> ℯ:π
2.718281828459045:1.0:2.718281828459045


"""
@irrational sym val def
@irrational(sym, val, def)
Expand Down Expand Up @@ -203,6 +234,3 @@ function alignment(io::IO, x::AbstractIrrational)
m === nothing ? (length(sprint(show, x, context=io, sizehint=0)), 0) :
(length(m.captures[1]), length(m.captures[2]))
end

# inv
inv(x::AbstractIrrational) = 1/x
2 changes: 1 addition & 1 deletion test/complex.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1008,7 +1008,7 @@ end
@testset "Complex Irrationals, issue #21204" begin
for x in (pi, ℯ, Base.MathConstants.catalan) # No need to test all of them
z = Complex(x, x)
@test typeof(z) == Complex{typeof(x)}
@test typeof(z) == Complex{Float64}
@test exp(z) ≈ exp(x) * cis(x)
@test log1p(z) ≈ log(1 + z)
@test exp2(z) ≈ exp(z * log(2))
Expand Down
74 changes: 70 additions & 4 deletions test/numbers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2075,10 +2075,10 @@ for T = (UInt8,Int8,UInt16,Int16,UInt32,Int32,UInt64,Int64,UInt128,Int128)
end

@testset "Irrational/Bool multiplication" begin
@test false*pi === 0.0
@test pi*false === 0.0
@test true*pi === Float64(pi)
@test pi*true === Float64(pi)
@test false*pi === false
@test pi*false === false
@test true*pi === pi
@test pi*true === pi
end
# issue #5492
@test -0.0 + false === -0.0
Expand Down Expand Up @@ -2699,6 +2699,72 @@ end
end
end

@testset "Irrational/Bool addition/subtraction" begin
@test false + pi === pi
@test pi + false === pi
@test true + pi === pi + 1.0
@test pi + true === pi + 1.0

@test false - pi === -pi
@test pi - false === pi
@test true - pi === 1.0 - pi
@test pi - true === pi - 1.0
end

@testset "One-argument functions of Irrational" begin
for f in (*, +, abs, adjoint, conj, prod, real, sum)
@test f(pi) === pi
end
for f in (isfinite, isreal, one)
@test f(pi) === true
end
for f in (imag, isinf, isinteger, ismissing, isnan, isnothing, isone,
iszero, signbit, zero)
@test f(pi) === false
end
for f in (sin, tan)
@test f(float(pi)) < eps() # otherwise f should not be on this list
# f(Float64(pi)) is supposed to be different from zero, but f(pi) is not.
@test f(pi) === 0.0
end
for f in (cot, csc)
@test f(pi) === Inf
end
@test cis(pi) === -1.0 + 0.0im
@test sincos(pi) === (sin(pi), cos(pi))
for f in (-, abs2, acosh, acot, acotd, acoth, acsc, acscd, acsch, angle,
asec, asecd, asinh, atan, atand, cbrt, ceil, cos, cosc, cosd,
cosh, cospi, cotd, coth, cscd, csch, deg2rad, exp, exp10, exp2,
expm1, floor, hypot, inv, log, log10, log1p, log2, rad2deg,
round, sec, secd, sech, sign, sinc, sind, sinh, sinpi, sqrt,
tand, tanh, trunc)
@test f(pi) isa Float64
@test isapprox(f(pi), Float64(f(big(pi))))
end
@test reim(pi) === (pi, false)
@test_throws ArgumentError oneunit(pi)
end

@testset "Two-argument functions of Irrationals" begin
for i in (ℯ, pi), j in (ℯ, π)
for f in (Complex, ComplexF16, ComplexF32, ComplexF64,
cld, cmp, complex, fld,
isapprox, isequal, isless, issetequal, issubset)
@test f(i, j) === f(float(i), float(j))
end
for f in (atan, atand, copysign, div, fld, flipsign,
hypot, kron, log, max, min,
mod, mod1, nextpow, prevpow, rem)
@test isapprox(f(i, j), Float64(f(big(i), big(j))))
end
for f in (divrem, fldmod, minmax)
@test all(isapprox.(f(i, j), Float64.(f(big(i), big(j)))))
end
@test isapprox(widemul(i, j), big(i)*big(j))
@test typeof(widemul(i, j)) == typeof(widemul(float(i), float(j)))
end
end

@testset "constructor inferability for $T" for T in [AbstractFloat, #=BigFloat,=# Float16,
Float32, Float64, Integer, Bool, Signed, BigInt, Int128, Int16, Int32, Int64, Int8,
Unsigned, UInt128, UInt16, UInt32, UInt64, UInt8]
Expand Down