Skip to content

Commit

Permalink
More methods for Irrational
Browse files Browse the repository at this point in the history
Define `zero(Irrational)` and `one(Irrational)` to return `false`
and `true` respectively. (This is breaking if someone is currently
using `true * pi` or `pi + false` as a way of forcing conversion to
`Float64`.)

Make `false` act as the additive identity for `Irrational` by defining
a method for `+`. (Not type stable)

Make `true` act as the multiplicative identity for `Irrational` by
defining a method for `*` (Not type stable)

Make `widemul` of two `Irrational`s return a `BigFloat`

Make `sin(pi)` and `tan(pi)` return exactly 0.0

Define a number of methods on `Irrational` to default to conversion to
Float64.
  • Loading branch information
perrutquist committed Feb 16, 2020
1 parent 8eb0f9f commit 020d8ee
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 10 deletions.
43 changes: 38 additions & 5 deletions base/irrationals.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,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 @@ -36,6 +42,7 @@ promote_rule(::Type{S}, ::Type{T}) where {S<:AbstractIrrational,T<:Number} = pro
AbstractFloat(x::AbstractIrrational) = Float64(x)
Float16(x::AbstractIrrational) = Float16(Float32(x))
Complex{T}(x::AbstractIrrational) where {T<:Real} = Complex{T}(T(x))
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 @@ -136,14 +143,43 @@ hash(x::Irrational, h::UInt) = 3*objectid(x) - h

widen(::Type{T}) where {T<:Irrational} = T

zero(::AbstractIrrational) = false
zero(::Type{<:AbstractIrrational}) = false

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)
*(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."))
Math.mod2pi(x::AbstractIrrational) = Float64(mod2pi(big(x)))
Math.rem2pi(x::AbstractIrrational, m) = Float64(rem2pi(big(x), m))

# 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)

"""
@irrational sym val def
@irrational(sym, val, def)
Expand Down Expand Up @@ -188,6 +224,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 @@ -991,7 +991,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
75 changes: 71 additions & 4 deletions test/numbers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2058,10 +2058,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 @@ -2655,3 +2655,70 @@ end
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

0 comments on commit 020d8ee

Please sign in to comment.