diff --git a/Project.toml b/Project.toml index c16d012..7b4d0bd 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "IrrationalConstants" uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" authors = ["JuliaMath"] -version = "0.2.2" +version = "0.2.3" [compat] julia = "1" diff --git a/src/macro.jl b/src/macro.jl index f414745..9b2a965 100644 --- a/src/macro.jl +++ b/src/macro.jl @@ -34,13 +34,19 @@ if VERSION < v"1.2.0-DEV.337" Base.inv(x::IrrationalConstant) = 1/x end +# https://github.com/JuliaLang/julia/pull/50894 +Base.typemin(::Type{T}) where {T<:IrrationalConstant} = T() +Base.typemax(::Type{T}) where {T<:IrrationalConstant} = T() + """ - @irrational sym val def [T] - @irrational(sym, val, def[, T]) + @irrational sym [val] def [T] + +Define an instance named `sym` of a new singleton type `T` representing an irrational constant as subtype of +`IrrationalConstants.IrrationalConstant <: AbstractIrrational`, +with arbitrary-precision definition in terms of `BigFloat`s given by the expression `def`. -Define a new singleton type `T` representing an irrational constant as subtype of -`IrrationalConstants.IrrationalConstant <: AbstractIrrational` with an instance named `sym`, pre-computed `Float64` value `val`, -and arbitrary-precision definition in terms of `BigFloat`s given by the expression `def`. +Optionally provide a pre-computed `Float64` value `val` which must equal `Float64(def)`. +It will be computed automatically if omitted. As default, `T` is set to `sym` with the first character converted to uppercase. @@ -49,30 +55,36 @@ returns `false`. # Examples -```jldoctest -julia> IrrationalConstants.@irrational(twoπ, 6.2831853071795864769, 2*big(π)) +```jldoctest; setup = :(import IrrationalConstants) +julia> IrrationalConstants.@irrational twoπ 2*big(π) julia> twoπ twoπ = 6.2831853071795... -julia> IrrationalConstants.@irrational sqrt2 1.4142135623730950488 √big(2) +julia> IrrationalConstants.@irrational sqrt2 1.4142135623730950488 √big(2) julia> sqrt2 sqrt2 = 1.4142135623730... -julia> IrrationalConstants.@irrational halfτ 3.14159265358979323846 pi +julia> IrrationalConstants.@irrational halfτ 3.14159265358979323846 pi julia> halfτ halfτ = 3.1415926535897... -julia> IrrationalConstants.@irrational sqrt2 1.4142135623730950488 big(2) -ERROR: AssertionError: big($(Expr(:escape, :sqrt2))) isa BigFloat +julia> IrrationalConstants.@irrational sqrt3 1.7320508075688772 big(3) +ERROR: AssertionError: big($(Expr(:escape, :sqrt3))) isa BigFloat -julia> IrrationalConstants.@irrational sqrt2 1.41421356237309 √big(2) -ERROR: AssertionError: Float64($(Expr(:escape, :sqrt2))) == Float64(big($(Expr(:escape, :sqrt2)))) +julia> IrrationalConstants.@irrational sqrt5 2.2360679775 √big(5) +ERROR: AssertionError: Float64($(Expr(:escape, :sqrt5))) == Float64(big($(Expr(:escape, :sqrt5)))) ``` """ -macro irrational(sym, val, def, T=Symbol(uppercasefirst(string(sym)))) +macro irrational(sym::Symbol, val::Float64, def::Union{Symbol,Expr}, T::Symbol=Symbol(uppercasefirst(string(sym)))) + irrational(sym, val, def, T) +end +macro irrational(sym::Symbol, def::Union{Symbol,Expr}, T::Symbol=Symbol(uppercasefirst(string(sym)))) + irrational(sym, :(big($(esc(sym)))), def, T) +end +function irrational(sym::Symbol, val::Union{Float64,Expr}, def::Union{Symbol,Expr}, T::Symbol) esym = esc(sym) qsym = esc(Expr(:quote, sym)) eT = esc(T) @@ -90,17 +102,23 @@ macro irrational(sym, val, def, T=Symbol(uppercasefirst(string(sym)))) end else # newer Julia versions - isa(def, Symbol) ? quote - function Base.BigFloat(::$eT, r::Base.MPFR.MPFRRoundingMode=Base.MPFR.ROUNDING_MODE[]; precision=precision(BigFloat)) - c = BigFloat(; precision=precision) - ccall(($(string("mpfr_const_", def)), :libmpfr), - Cint, (Ref{BigFloat}, Base.MPFR.MPFRRoundingMode), c, r) - return c + if isa(def, Symbol) + # support older Julia versions prior to https://github.com/JuliaLang/julia/pull/51362 + r = VERSION < v"1.12.0-DEV.78" ? :(Base.MPFR.ROUNDING_MODE[]) : :(Rounding.rounding_raw(BigFloat)) + quote + function Base.BigFloat(::$eT, r::Base.MPFR.MPFRRoundingMode=$r; precision=precision(BigFloat)) + c = BigFloat(; precision=precision) + ccall(($(string("mpfr_const_", def)), :libmpfr), + Cint, (Ref{BigFloat}, Base.MPFR.MPFRRoundingMode), c, r) + return c + end end - end : quote - function Base.BigFloat(::$eT; precision=precision(BigFloat)) - setprecision(BigFloat, precision) do - $(esc(def)) + else + quote + function Base.BigFloat(::$eT; precision=precision(BigFloat)) + setprecision(BigFloat, precision) do + $(esc(def)) + end end end end @@ -109,8 +127,10 @@ macro irrational(sym, val, def, T=Symbol(uppercasefirst(string(sym)))) struct $T <: IrrationalConstant end const $esym = $eT() $bigconvert - Base.Float64(::$eT) = $val - Base.Float32(::$eT) = $(Float32(val)) + let v = $val, v64 = Float64(v), v32 = Float32(v) + Base.Float64(::$eT) = v64 + Base.Float32(::$eT) = v32 + end Base.show(io::IO, ::$eT) = print(io, $qsym) @assert isa(big($esym), BigFloat) @assert Float64($esym) == Float64(big($esym)) diff --git a/src/stats.jl b/src/stats.jl index c221961..dac62a1 100644 --- a/src/stats.jl +++ b/src/stats.jl @@ -1,30 +1,30 @@ # mathematical constants related to statistics -@irrational twoπ 6.2831853071795864769 2 * big(π) -@irrational fourπ 12.566370614359172954 4 * big(π) -@irrational halfπ 1.5707963267948966192 big(π) / 2 -@irrational quartπ 0.7853981633974483096 big(π) / 4 +@irrational twoπ 2 * big(π) +@irrational fourπ 4 * big(π) +@irrational halfπ big(π) / 2 +@irrational quartπ big(π) / 4 -@irrational invπ 0.31830988618379067154 inv(big(π)) -@irrational twoinvπ 0.63661977236758134308 2 / big(π) -@irrational fourinvπ 1.27323954473516268615 4 / big(π) -@irrational inv2π 0.159154943091895335769 inv(2 * big(π)) -@irrational inv4π 0.079577471545947667884 inv(4 * big(π)) +@irrational invπ inv(big(π)) +@irrational twoinvπ 2 / big(π) +@irrational fourinvπ 4 / big(π) +@irrational inv2π inv(2 * big(π)) +@irrational inv4π inv(4 * big(π)) -@irrational sqrt2 1.4142135623730950488 sqrt(big(2)) -@irrational sqrt3 1.7320508075688772935 sqrt(big(3)) -@irrational sqrtπ 1.7724538509055160273 sqrt(big(π)) -@irrational sqrt2π 2.5066282746310005024 sqrt(2 * big(π)) -@irrational sqrt4π 3.5449077018110320546 sqrt(4 * big(π)) -@irrational sqrthalfπ 1.2533141373155002512 sqrt(big(π) / 2) +@irrational sqrt2 sqrt(big(2)) +@irrational sqrt3 sqrt(big(3)) +@irrational sqrtπ sqrt(big(π)) +@irrational sqrt2π sqrt(2 * big(π)) +@irrational sqrt4π sqrt(4 * big(π)) +@irrational sqrthalfπ sqrt(big(π) / 2) -@irrational invsqrt2 0.7071067811865475244 inv(sqrt(big(2))) -@irrational invsqrtπ 0.5641895835477563 inv(sqrt(big(π))) -@irrational invsqrt2π 0.3989422804014326779 inv(sqrt(2 * big(π))) +@irrational invsqrt2 inv(sqrt(big(2))) +@irrational invsqrtπ inv(sqrt(big(π))) +@irrational invsqrt2π inv(sqrt(2 * big(π))) -@irrational loghalf -0.6931471805599453094 log(inv(big(2))) -@irrational logtwo 0.6931471805599453094 log2 -@irrational logten 2.302585092994046 log(big(10)) -@irrational logπ 1.1447298858494001741 log(big(π)) -@irrational log2π 1.8378770664093454836 log(2 * big(π)) -@irrational log4π 2.5310242469692907930 log(4 * big(π)) +@irrational loghalf log(inv(big(2))) +@irrational logtwo log2 +@irrational logten log(big(10)) +@irrational logπ log(big(π)) +@irrational log2π log(2 * big(π)) +@irrational log4π log(4 * big(π)) diff --git a/test/runtests.jl b/test/runtests.jl index a529a5e..9d47166 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2,6 +2,11 @@ using IrrationalConstants using Documenter using Test +const ALLCONSTANTS = filter!( + x -> x isa IrrationalConstants.IrrationalConstant, + map(Base.Fix1(getproperty, IrrationalConstants), names(IrrationalConstants)), +) + @testset "k*pi" begin @test isapprox(2*pi, twoπ) @test isapprox(4*pi, fourπ) @@ -48,30 +53,47 @@ end end @testset "hash" begin - for i in (twoπ, invπ, sqrt2, logtwo), j in (twoπ, invπ, sqrt2, logtwo) + for i in ALLCONSTANTS, j in ALLCONSTANTS @test isequal(i==j, hash(i)==hash(j)) end end @testset "doctests" begin - DocMeta.setdocmeta!( - IrrationalConstants, :DocTestSetup, :(using IrrationalConstants); recursive=true - ) doctest(IrrationalConstants; manual=false) end # copied from https://github.com/JuliaLang/julia/blob/cf5ae0369ceae078cf6a29d7aa34f48a5a53531e/test/numbers.jl # and adapted to irrationals in this package -@testset "IrrationalConstant zero and one" begin - @test one(twoπ) === true - @test zero(twoπ) === false - @test one(typeof(twoπ)) === true - @test zero(typeof(twoπ)) === false +@testset "IrrationalConstants zero and one" begin + for i in ALLCONSTANTS + @test one(i) === true + @test zero(i) === false + @test one(typeof(i)) === true + @test zero(typeof(i)) === false + end +end + +@testset "IrrationalConstants iszero, isfinite, isinteger, and isone" begin + for i in ALLCONSTANTS + @test !iszero(i) + @test !isone(i) + @test !isinteger(i) + @test isfinite(i) + end +end + +@testset "IrrationalConstants promote_type" begin + for T in (Float16, Float32, Float64) + for i in ALLCONSTANTS + @test T(2.0) * i ≈ T(2.0) * T(i) + @test T(2.0) * i isa T + end + end end @testset "IrrationalConstants compared with IrrationalConstants" begin - for i in (twoπ, invπ, sqrt2, logtwo), j in (twoπ, invπ, sqrt2, logtwo) + for i in ALLCONSTANTS, j in ALLCONSTANTS @test isequal(i==j, Float64(i)==Float64(j)) @test isequal(i!=j, Float64(i)!=Float64(j)) @test isequal(i<=j, Float64(i)<=Float64(j)) @@ -81,29 +103,31 @@ end end end -@testset "IrrationalConstant Inverses, JuliaLang/Julia Issue #30882" begin +@testset "IrrationalConstants Inverses, JuliaLang/Julia Issue #30882" begin @test @inferred(inv(twoπ)) ≈ 0.15915494309189535 end @testset "IrrationalConstants compared with Rationals and Floats" begin - @test Float64(twoπ, RoundDown) < twoπ - @test Float64(twoπ, RoundUp) > twoπ - @test !(Float64(twoπ, RoundDown) > twoπ) - @test !(Float64(twoπ, RoundUp) < twoπ) - @test Float64(twoπ, RoundDown) <= twoπ - @test Float64(twoπ, RoundUp) >= twoπ - @test Float64(twoπ, RoundDown) != twoπ - @test Float64(twoπ, RoundUp) != twoπ - - @test Float32(twoπ, RoundDown) < twoπ - @test Float32(twoπ, RoundUp) > twoπ - @test !(Float32(twoπ, RoundDown) > twoπ) - @test !(Float32(twoπ, RoundUp) < twoπ) - - @test prevfloat(big(twoπ)) < twoπ - @test nextfloat(big(twoπ)) > twoπ - @test !(prevfloat(big(twoπ)) > twoπ) - @test !(nextfloat(big(twoπ)) < twoπ) + for i in ALLCONSTANTS + @test Float64(i, RoundDown) < i + @test Float64(i, RoundUp) > i + @test !(Float64(i, RoundDown) > i) + @test !(Float64(i, RoundUp) < i) + @test Float64(i, RoundDown) <= i + @test Float64(i, RoundUp) >= i + @test Float64(i, RoundDown) != i + @test Float64(i, RoundUp) != i + + @test Float32(i, RoundDown) < i + @test Float32(i, RoundUp) > i + @test !(Float32(i, RoundDown) > i) + @test !(Float32(i, RoundUp) < i) + + @test prevfloat(big(i)) < i + @test nextfloat(big(i)) > i + @test !(prevfloat(big(i)) > i) + @test !(nextfloat(big(i)) < i) + end @test 5293386250278608690//842468587426513207 < twoπ @test !(5293386250278608690//842468587426513207 > twoπ) @@ -181,3 +205,50 @@ end @test sec(quartπ) === Float64(sec(big(quartπ))) @test cot(quartπ) === Float64(cot(big(quartπ))) end + +# Ref https://github.com/JuliaLang/julia/pull/46054 +IrrationalConstants.@irrational irrational_1548_pi 4863.185427757 1548big(pi) +IrrationalConstants.@irrational irrational_inv_1548_pi 1/big(irrational_1548_pi) +@testset "IrrationalConstants.@irrational" begin + @test irrational_1548_pi ≈ 1548big(pi) + @test Float64(irrational_1548_pi) == 1548π + @test irrational_1548_pi ≈ 1548pi + @test irrational_1548_pi != 1548pi + @test irrational_inv_1548_pi ≈ inv(1548big(pi)) + @test Float64(irrational_inv_1548_pi) == 1/(1548π) + @test irrational_inv_1548_pi ≈ inv(1548pi) + @test irrational_inv_1548_pi != inv(1548pi) +end + +# Ref https://github.com/JuliaLang/julia/pull/50894 +@testset "irrational special values" begin + for v ∈ ALLCONSTANTS + @test v === typemin(v) === typemax(v) + end +end + +# Ref https://github.com/JuliaLang/julia/pull/55911 +@testset "logtwo to `BigFloat` with `setrounding`" begin + function irrational_to_big_float(c::AbstractIrrational) + BigFloat(c) + end + + function irrational_to_big_float_with_rounding_mode(c::AbstractIrrational, rm::RoundingMode) + f = () -> irrational_to_big_float(c) + setrounding(f, BigFloat, rm) + end + + function irrational_to_big_float_with_rounding_mode_and_precision(c::AbstractIrrational, rm::RoundingMode, prec::Int) + f = () -> irrational_to_big_float_with_rounding_mode(c, rm) + setprecision(f, BigFloat, prec) + end + + # logtwo is the only constant defined based on an MPFR constant (similar to π, γ, catalan) + c = logtwo + for p ∈ 1:40 + @test ( + irrational_to_big_float_with_rounding_mode_and_precision(c, RoundDown, p) < c < + irrational_to_big_float_with_rounding_mode_and_precision(c, RoundUp, p) + ) + end +end