From d9389f00ebb1d958d5e5e955dfb041b7fb0b43fc Mon Sep 17 00:00:00 2001 From: Joel Dahne Date: Tue, 9 Jan 2024 16:17:32 +0100 Subject: [PATCH 01/16] Add typemin and typemax methods --- src/float.jl | 8 ++++++++ test/float.jl | 15 +++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/float.jl b/src/float.jl index 7ab0a1da..0527290f 100644 --- a/src/float.jl +++ b/src/float.jl @@ -16,3 +16,11 @@ function Base.eps(T::Type{<:Union{ArfOrRef,ArbOrRef}}) return eps!(res, res) end Base.eps(x::Union{ArfOrRef,ArbOrRef}) = eps!(zero(x), x) + +Base.typemin(::Type{<:MagOrRef}) = zero!(Mag()) +Base.typemin(x::Union{ArfOrRef,ArbOrRef}) = neg_inf!(zero(x)) +Base.typemin(T::Type{<:Union{ArfOrRef,ArbOrRef}}) = neg_inf!(zero(T)) + +Base.typemax(::Type{<:MagOrRef}) = inf!(Mag()) +Base.typemax(x::Union{ArfOrRef,ArbOrRef}) = pos_inf!(zero(x)) +Base.typemax(T::Type{<:Union{ArfOrRef,ArbOrRef}}) = pos_inf!(zero(T)) diff --git a/test/float.jl b/test/float.jl index 1145ca21..a6eeb065 100644 --- a/test/float.jl +++ b/test/float.jl @@ -42,4 +42,19 @@ @test isnan(eps(Arf(NaN))) @test isnan(eps(Arb(NaN))) end + + @testset "typemin/typemax" begin + @test typemin(Mag) == typemin(Mag(5)) == Mag(0) + @test typemax(Mag) == typemax(Mag(5)) == Mag(Inf) + + @test typemin(Arf) == typemin(Arf(5)) == Arf(-Inf) + @test typemax(Arf) == typemax(Arf(5)) == Arf(Inf) + @test precision(typemin(Arf(prec = 80))) == 80 + @test precision(typemax(Arf(prec = 80))) == 80 + + @test typemin(Arb) == typemin(Arb(5)) == Arb(-Inf) + @test typemax(Arb) == typemax(Arb(5)) == Arb(Inf) + @test precision(typemin(Arb(prec = 80))) == 80 + @test precision(typemax(Arb(prec = 80))) == 80 + end end From 2a5c688308e90b861f248d35369eee0a24a70636 Mon Sep 17 00:00:00 2001 From: Joel Dahne Date: Tue, 9 Jan 2024 17:40:14 +0100 Subject: [PATCH 02/16] Add frexp and ldexp for Arf and Arb --- src/float.jl | 23 +++++++++++++++++++++++ test/float.jl | 24 ++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/src/float.jl b/src/float.jl index 0527290f..a365cd1e 100644 --- a/src/float.jl +++ b/src/float.jl @@ -24,3 +24,26 @@ Base.typemin(T::Type{<:Union{ArfOrRef,ArbOrRef}}) = neg_inf!(zero(T)) Base.typemax(::Type{<:MagOrRef}) = inf!(Mag()) Base.typemax(x::Union{ArfOrRef,ArbOrRef}) = pos_inf!(zero(x)) Base.typemax(T::Type{<:Union{ArfOrRef,ArbOrRef}}) = pos_inf!(zero(T)) + +function Base.frexp(x::ArfOrRef) + m = zero(x) + e = fmpz_struct() + ccall( + @libflint(arf_frexp), + Nothing, + (Ref{arf_struct}, Ref{fmpz_struct}, Ref{arf_struct}), + m, + e, + x, + ) + + return m, BigInt(e) +end + +function Base.frexp(x::ArbOrRef) + # Compute for midpoint and just scale radius + _, e = frexp(midref(x)) + return ldexp(x, -e), e +end + +Base.ldexp(x::Union{ArfOrRef,ArbOrRef}, n::Integer) = mul_2exp!(zero(x), x, n) diff --git a/test/float.jl b/test/float.jl index a6eeb065..711b6c76 100644 --- a/test/float.jl +++ b/test/float.jl @@ -57,4 +57,28 @@ @test precision(typemin(Arb(prec = 80))) == 80 @test precision(typemax(Arb(prec = 80))) == 80 end + + @testset "frexp/ldexp" begin + @test frexp(Arf(12.3)) == frexp(12.3) + @test frexp(Arf(-12.3)) == frexp(-12.3) + @test frexp(Arf(0)) == frexp(0.0) + @test frexp(Arf(Inf)) == frexp(Inf) + @test precision(frexp(Arf(1, prec = 80))[1]) == 80 + @test frexp(Arf(1)) isa Tuple{Arf,BigInt} + + @test frexp(Arb(12.3)) == frexp(12.3) + @test frexp(Arb(-12.3)) == frexp(-12.3) + @test frexp(Arb(0)) == frexp(0.0) + @test frexp(Arb(Inf)) == frexp(Inf) + @test isequal(frexp(Arb(π))[1], Arblib.mul_2exp!(Arb(), Arb(π), -2)) + @test precision(frexp(Arb(1, prec = 80))[1]) == 80 + @test frexp(Arb(1)) isa Tuple{Arb,BigInt} + + @test ldexp(Arf(1.1), 2) == Arf(1.1) * 2^2 + @test ldexp(Arf(1.1), -10) == Arf(1.1) / 2^10 + @test ldexp(Arb(1.1), 2) == Arb(1.1) * 2^2 + @test ldexp(Arb(1.1), -10) == Arb(1.1) / 2^10 + @test precision(ldexp(Arf(1, prec = 80), 1)) == 80 + @test precision(ldexp(Arb(1, prec = 80), 1)) == 80 + end end From 020ae271abf3e81b6dafe9b240512427b0f9e8ee Mon Sep 17 00:00:00 2001 From: Joel Dahne Date: Tue, 16 Jan 2024 09:22:53 +0100 Subject: [PATCH 03/16] Improve conversion to Float16/32/64, BigFloat and Complex It now correctly handles rounding in all cases and allows specifying the precision for BigFloat. Note that for BigFloat it uses different default values than the constructors in Base. I always defaults to RoundNearest and uses the precision given by the input argument. This means that it doesn't depend on the global rounding mode and precision. For Complex it now supports conversion from Acb to any Complex{T}, as long as convert(T, ::ArbRef) is defined. --- src/constructors.jl | 16 ++--- src/float.jl | 56 +++++++++++++++ test/constructors.jl | 33 ++++----- test/float.jl | 161 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 238 insertions(+), 28 deletions(-) diff --git a/src/constructors.jl b/src/constructors.jl index e3eb3a2a..4fc4e2b3 100644 --- a/src/constructors.jl +++ b/src/constructors.jl @@ -69,18 +69,10 @@ end Base.Int(x::ArfOrRef; rnd::Union{arb_rnd,RoundingMode} = RoundNearest) = is_int(x) ? get_si(x, rnd) : throw(InexactError(:Int64, Int64, x)) -Base.Float64(x::MagOrRef) = get(x) -Base.Float64(x::ArfOrRef; rnd::Union{arb_rnd,RoundingMode} = RoundNearest) = get_d(x, rnd) -Base.Float64(x::ArbOrRef) = Float64(midref(x)) - -Base.ComplexF64(z::AcbOrRef) = Complex(Float64(realref(z)), Float64(imagref(z))) - -function Base.BigFloat(x::ArfOrRef) - y = BigFloat(; precision = precision(x)) - get!(y, x) - return y -end -Base.BigFloat(x::ArbOrRef) = BigFloat(midref(x)) +# TODO: This currently allows construction of Complex{ArbRef}, which +# we probably don't want. +Base.Complex{T}(z::AcbOrRef) where {T} = Complex{T}(realref(z), imagref(z)) +Base.Complex(z::AcbOrRef) = Complex{Arb}(z) Base.zero(::Union{Mag,Type{Mag}}) = Mag(UInt64(0)) Base.one(::Union{Mag,Type{Mag}}) = Mag(UInt64(1)) diff --git a/src/float.jl b/src/float.jl index a365cd1e..e8adb13f 100644 --- a/src/float.jl +++ b/src/float.jl @@ -1,3 +1,59 @@ +# Conversion to float types in Base + +## Float64 +Base.Float64(x::MagOrRef, r::RoundingMode = RoundUp) = Float64(x, convert(arb_rnd, r)) +Base.Float64(x::ArfOrRef, r::RoundingMode) = Float64(x, convert(arb_rnd, r)) +Base.Float64(x::ArbOrRef, r::RoundingMode = RoundNearest) = Float64(x, convert(arb_rnd, r)) + +function Base.Float64(x::MagOrRef, r::arb_rnd) + r == ArbRoundUp || + throw(ArgumentError("only supports rounding up when converting Mag to Float64")) + return get(x) +end +Base.Float64(x::ArfOrRef, r::arb_rnd) = get_d(x, r) +Base.Float64(x::ArbOrRef, r::arb_rnd) = Float64(midref(x), r) + +# Deprecated +# TODO: This signature clashes with the above one... +Base.Float64(x::ArfOrRef; rnd::arb_rnd = ArbRoundNearest) = Float64(x, rnd) + +## Float16 and Float32 +Base.Float16(x::MagOrRef, r::RoundingMode = RoundUp) = Float16(Float64(x, r), r) +Base.Float32(x::MagOrRef, r::RoundingMode = RoundUp) = Float32(Float64(x, r), r) +Base.Float16(x::Union{ArfOrRef,ArbOrRef}, r::RoundingMode = RoundNearest) = + Float16(Float64(x, r), r) +Base.Float32(x::Union{ArfOrRef,ArbOrRef}, r::RoundingMode = RoundNearest) = + Float32(Float64(x, r), r) + +## BigFloat + +# Note that this uses different default values than the constructors +# in Base. I always defaults to RoundNearest and uses the precision +# given by the input argument. This means that it doesn't depend on +# the global rounding mode and precision. + +Base.BigFloat(x::Union{ArfOrRef,ArbOrRef}, r::RoundingMode; precision = Base.precision(x)) = + BigFloat(x, convert(Base.MPFR.MPFRRoundingMode, r); precision) + +Base.BigFloat(x::Union{ArfOrRef,ArbOrRef}, r::arb_rnd; precision = Base.precision(x)) = + BigFloat(x, convert(RoundingMode, r); precision) + +function Base.BigFloat( + x::ArfOrRef, + r::Base.MPFR.MPFRRoundingMode = Base.MPFR.RoundNearest; + precision = Base.precision(x), +) + y = BigFloat(; precision) + get!(y, x, r) + return y +end +Base.BigFloat( + x::ArbOrRef, + r::Base.MPFR.MPFRRoundingMode = Base.MPFR.RoundNearest; + precision = Base.precision(x), +) = BigFloat(midref(x), r; precision) + +# Common methods in the AbstractFloat interface function eps!(res::ArfOrRef, x::ArfOrRef) isspecial(x) && return nan!(res) return set!(res, set_ulp!(Mag(), x, prec = precision(x))) diff --git a/test/constructors.jl b/test/constructors.jl index 5ee089c8..a825518c 100644 --- a/test/constructors.jl +++ b/test/constructors.jl @@ -129,25 +129,26 @@ end @testset "Conversion" begin - @test Int(Arf(2.0)) isa Int - @test Int(Arf(2.0)) == 2 - @test_throws InexactError Int(Arf(2.5)) + @testset "Int" begin + @test Int(Arf(2.0)) isa Int + @test Int(Arf(2.0)) == 2 + @test_throws InexactError Int(Arf(2.5)) - @test Float64(Mag(2)) isa Float64 - @test Float64(Mag(2)) == 2.0 - @test Float64(Arf(2)) isa Float64 - @test Float64(Arf(2)) == 2.0 - @test Float64(Arb(2)) isa Float64 - @test Float64(Arb(2)) == 2.0 - - @test ComplexF64(Acb(2 + 3im)) isa ComplexF64 - @test ComplexF64(Acb(2 + 3im)) == 2.0 + 3.0im + @test Arblib.get_si(Arf(0.5)) == 0 + @test Arblib.get_si(Arf(0.5); rnd = Arblib.ArbRoundFromZero) == 1 + end - @test Arblib.get_si(Arf(0.5)) == 0 - @test Arblib.get_si(Arf(0.5); rnd = Arblib.ArbRoundFromZero) == 1 + @testset "Complex" begin + @test Complex{Float32}(Acb(2 + 3im)) isa Complex{Float32} + @test Complex{Float64}(Acb(2 + 3im)) isa Complex{Float64} + @test Complex{Arb}(Acb(2 + 3im)) isa Complex{Arb} + @test Complex(Acb(2 + 3im)) isa Complex{Arb} - @test BigFloat(Arf(2.5)) == 2.5 - @test BigFloat(Arb(2.5)) == 2.5 + @test Complex{Float32}(Acb(2 + 3im)) == 2.0 + 3.0im + @test Complex{Float64}(Acb(2 + 3im)) == 2.0 + 3.0im + @test Complex{Arb}(Acb(2 + 3im)) == 2.0 + 3.0im + @test Complex(Acb(2 + 3im)) == 2.0 + 3.0im + end end @testset "zeros/ones" begin diff --git a/test/float.jl b/test/float.jl index 711b6c76..a9a7fdde 100644 --- a/test/float.jl +++ b/test/float.jl @@ -1,4 +1,165 @@ @testset "float" begin + @testset "Conversion" begin + ArbRoundNearest = Arblib.RoundNearest + ArbRoundDown = Arblib.RoundDown + ArbRoundUp = Arblib.RoundUp + + @testset "$T" for T in (Float16, Float32, Float64) + # Mag + @test T(Mag(2)) isa T + @test π < T(Mag(π)) == T(Mag(π), RoundUp) == T(Mag(π), ArbRoundUp) < 3.15 + + @test_throws ArgumentError T(Mag(1), RoundDown) + @test_throws ArgumentError T(Mag(1), ArbRoundDown) + @test_throws ArgumentError T(Mag(1), RoundNearest) + @test_throws ArgumentError T(Mag(1), ArbRoundNearest) + + # Arf + @test T(Arf(2)) isa T + @test T(Arf(2)) == 2 + + @test T(1 + Arf(eps(T)) / 3) == T(1) + @test T(1 + Arf(eps(T)) / 3, RoundNearest) == T(1) + @test T(1 + Arf(eps(T)) / 3, ArbRoundNearest) == T(1) + @test T(1 + 2Arf(eps(T)) / 3, RoundNearest) == nextfloat(T(1)) + @test T(1 + 2Arf(eps(T)) / 3, ArbRoundNearest) == nextfloat(T(1)) + @test T(1 + Arf(eps(T)) / 3, RoundDown) == T(1) + @test T(1 + Arf(eps(T)) / 3, ArbRoundDown) == T(1) + @test T(1 + Arf(eps(T)) / 3, RoundUp) == nextfloat(T(1)) + @test T(1 + Arf(eps(T)) / 3, ArbRoundUp) == nextfloat(T(1)) + + # Arb + @test T(Arb(2)) isa T + @test T(Arb(2)) == 2 + + @test T(1 + Arb(eps(T)) / 3) == T(1) + @test T(1 + Arb(eps(T)) / 3, RoundNearest) == T(1) + @test T(1 + Arb(eps(T)) / 3, ArbRoundNearest) == T(1) + @test T(1 + 2Arb(eps(T)) / 3, RoundNearest) == nextfloat(T(1)) + @test T(1 + 2Arb(eps(T)) / 3, ArbRoundNearest) == nextfloat(T(1)) + @test T(1 + Arb(eps(T)) / 3, RoundDown) == T(1) + @test T(1 + Arb(eps(T)) / 3, ArbRoundDown) == T(1) + @test T(1 + Arb(eps(T)) / 3, RoundUp) == nextfloat(T(1)) + @test T(1 + Arb(eps(T)) / 3, ArbRoundUp) == nextfloat(T(1)) + + @test T(setball(Arb, 1 + Arf(eps(T)) / 3, Mag(1))) == T(1) + @test T(setball(Arb, 1 + Arf(eps(T)) / 3, Mag(1)), RoundNearest) == T(1) + @test T(setball(Arb, 1 + Arf(eps(T)) / 3, Mag(1)), ArbRoundNearest) == T(1) + @test T(setball(Arb, 1 + 2Arf(eps(T)) / 3, Mag(1)), RoundNearest) == + nextfloat(T(1)) + @test T(setball(Arb, 1 + 2Arf(eps(T)) / 3, Mag(1)), ArbRoundNearest) == + nextfloat(T(1)) + @test T(setball(Arb, 1 + Arf(eps(T)) / 3, Mag(1)), RoundDown) == T(1) + @test T(setball(Arb, 1 + Arf(eps(T)) / 3, Mag(1)), ArbRoundDown) == T(1) + @test T(setball(Arb, 1 + Arf(eps(T)) / 3, Mag(1)), RoundUp) == nextfloat(T(1)) + @test T(setball(Arb, 1 + Arf(eps(T)) / 3, Mag(1)), ArbRoundUp) == + nextfloat(T(1)) + end + + @testset "BigFloat" begin + @test BigFloat(Arf(2)) isa BigFloat + @test BigFloat(Arf(2)) == 2 + + @test precision(BigFloat(Arf(2), precision = 80)) == 80 + @test precision(BigFloat(Arb(2), precision = 80)) == 80 + + let precision = 80, + ϵ = setprecision(eps(Arf(1, prec = precision)), 256), + M = Base.MPFR + + @test BigFloat(1 + ϵ / 3; precision) == 1 + @test BigFloat(1 + ϵ / 3, RoundNearest; precision) == 1 + @test BigFloat(1 + ϵ / 3, ArbRoundNearest; precision) == 1 + @test BigFloat(1 + ϵ / 3, M.MPFRRoundNearest; precision) == 1 + @test BigFloat(1 + 2ϵ / 3, RoundNearest; precision) == + nextfloat(BigFloat(1; precision)) + @test BigFloat(1 + 2ϵ / 3, ArbRoundNearest; precision) == + nextfloat(BigFloat(1; precision)) + @test BigFloat(1 + 2ϵ / 3, M.MPFRRoundNearest; precision) == + nextfloat(BigFloat(1; precision)) + @test BigFloat(1 + ϵ / 3, RoundDown; precision) == 1 + @test BigFloat(1 + ϵ / 3, ArbRoundDown; precision) == 1 + @test BigFloat(1 + ϵ / 3, M.MPFRRoundDown; precision) == 1 + @test BigFloat(1 + ϵ / 3, RoundUp; precision) == + nextfloat(BigFloat(1; precision)) + @test BigFloat(1 + ϵ / 3, ArbRoundUp; precision) == + nextfloat(BigFloat(1; precision)) + @test BigFloat(1 + ϵ / 3, M.MPFRRoundUp; precision) == + nextfloat(BigFloat(1; precision)) + end + + let precision = 80, + ϵ = setprecision(eps(Arb(1, prec = precision)), 256), + M = Base.MPFR + + @test BigFloat(1 + ϵ / 3; precision) == 1 + @test BigFloat(1 + ϵ / 3, RoundNearest; precision) == 1 + @test BigFloat(1 + ϵ / 3, ArbRoundNearest; precision) == 1 + @test BigFloat(1 + ϵ / 3, M.MPFRRoundNearest; precision) == 1 + @test BigFloat(1 + 2ϵ / 3, RoundNearest; precision) == + nextfloat(BigFloat(1; precision)) + @test BigFloat(1 + 2ϵ / 3, ArbRoundNearest; precision) == + nextfloat(BigFloat(1; precision)) + @test BigFloat(1 + 2ϵ / 3, M.MPFRRoundNearest; precision) == + nextfloat(BigFloat(1; precision)) + @test BigFloat(1 + ϵ / 3, RoundDown; precision) == 1 + @test BigFloat(1 + ϵ / 3, ArbRoundDown; precision) == 1 + @test BigFloat(1 + ϵ / 3, M.MPFRRoundDown; precision) == 1 + @test BigFloat(1 + ϵ / 3, RoundUp; precision) == + nextfloat(BigFloat(1; precision)) + @test BigFloat(1 + ϵ / 3, ArbRoundUp; precision) == + nextfloat(BigFloat(1; precision)) + @test BigFloat(1 + ϵ / 3, M.MPFRRoundUp; precision) == + nextfloat(BigFloat(1; precision)) + end + + let precision = 80, + ϵ = setprecision(eps(Arf(1, prec = precision)), 256), + M = Base.MPFR + + @test BigFloat(setball(Arb, 1 + ϵ / 3, Mag(1)); precision) == 1 + @test BigFloat(setball(Arb, 1 + ϵ / 3, Mag(1)), RoundNearest; precision) == + 1 + @test BigFloat( + setball(Arb, 1 + ϵ / 3, Mag(1)), + ArbRoundNearest; + precision, + ) == 1 + @test BigFloat( + setball(Arb, 1 + ϵ / 3, Mag(1)), + M.MPFRRoundNearest; + precision, + ) == 1 + @test BigFloat(setball(Arb, 1 + 2ϵ / 3, Mag(1)), RoundNearest; precision) == + nextfloat(BigFloat(1; precision)) + @test BigFloat( + setball(Arb, 1 + 2ϵ / 3, Mag(1)), + ArbRoundNearest; + precision, + ) == nextfloat(BigFloat(1; precision)) + @test BigFloat( + setball(Arb, 1 + 2ϵ / 3, Mag(1)), + M.MPFRRoundNearest; + precision, + ) == nextfloat(BigFloat(1; precision)) + @test BigFloat(setball(Arb, 1 + ϵ / 3, Mag(1)), RoundDown; precision) == 1 + @test BigFloat(setball(Arb, 1 + ϵ / 3, Mag(1)), ArbRoundDown; precision) == + 1 + @test BigFloat( + setball(Arb, 1 + ϵ / 3, Mag(1)), + M.MPFRRoundDown; + precision, + ) == 1 + @test BigFloat(setball(Arb, 1 + ϵ / 3, Mag(1)), RoundUp; precision) == + nextfloat(BigFloat(1; precision)) + @test BigFloat(setball(Arb, 1 + ϵ / 3, Mag(1)), ArbRoundUp; precision) == + nextfloat(BigFloat(1; precision)) + @test BigFloat(setball(Arb, 1 + ϵ / 3, Mag(1)), M.MPFRRoundUp; precision) == + nextfloat(BigFloat(1; precision)) + end + end + end + @testset "eps" begin @test one(Arf) + eps(Arf) > one(Arf) @test one(Arf) + eps(Arf) / 2 == one(Arf) From 49e1304882e952a1faa6408e4e5a088dde4e7593 Mon Sep 17 00:00:00 2001 From: Joel Dahne Date: Tue, 16 Jan 2024 09:28:10 +0100 Subject: [PATCH 04/16] radius: Add support for T = Float64 --- src/interval.jl | 3 ++- test/interval.jl | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/interval.jl b/src/interval.jl index aee09157..0f9c2b2c 100644 --- a/src/interval.jl +++ b/src/interval.jl @@ -5,11 +5,12 @@ export radius, radius([T, ] x::ArbOrRef) Returns the radius of `x` as a `Mag`. If `T` is given convert to this -type, supports `Mag`, `Arf` and `Arb`. +type, supports `Mag`, `Arf`, `Arb` and `Float64`. """ radius(::Type{Mag}, x::ArbOrRef) = Mag(radref(x)) radius(::Type{Arf}, x::ArbOrRef) = Arf(radref(x), prec = precision(x)) radius(::Type{Arb}, x::ArbOrRef) = Arb(radref(x), prec = precision(x)) +radius(::Type{Float64}, x::ArbOrRef) = Float64(radref(x)) radius(x::ArbOrRef) = radius(Mag, x) """ diff --git a/test/interval.jl b/test/interval.jl index 1cd1dfb5..e02060f0 100644 --- a/test/interval.jl +++ b/test/interval.jl @@ -4,13 +4,14 @@ y = Arb(2) Arblib.set!(Arblib.radref(y), one(Mag)) - @test radius(x) == radius(Mag, x) == radius(Arf, x) == radius(Arb, x) == zero(Mag) - @test radius(y) == radius(Mag, y) == radius(Arf, y) == radius(Arb, y) == one(Mag) + @test radius(x) == radius(Mag, x) == radius(Arf, x) == radius(Arb, x) == 0 + @test radius(y) == radius(Mag, y) == radius(Arf, y) == radius(Arb, y) == 1 @test radius(x) isa Mag @test radius(Mag, x) isa Mag @test radius(Arf, x) isa Arf @test radius(Arb, x) isa Arb + @test radius(Float64, x) isa Float64 @test precision(radius(Arf, Arb(prec = 80))) == 80 @test precision(radius(Arb, Arb(prec = 80))) == 80 From b8008e7c607f688134ccc52d66ce91b3b87c69e1 Mon Sep 17 00:00:00 2001 From: Joel Dahne Date: Tue, 16 Jan 2024 09:28:31 +0100 Subject: [PATCH 05/16] getball: Add support for T = Arf --- src/interval.jl | 3 ++- test/interval.jl | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/interval.jl b/src/interval.jl index 0f9c2b2c..e6adc57c 100644 --- a/src/interval.jl +++ b/src/interval.jl @@ -134,11 +134,12 @@ getinterval(x::ArbOrRef) = getinterval(Arf, x) Returns a tuple `(m::Arf, r::Mag)` where `m` is the midpoint of the ball and `r` is the radius. If `T` is given convert both `m` and `r` -to this type, supports `Arb`. +to this type, supports `Arf` and `Arb`. See also [`setball`](@ref) and [`getinterval`](@ref). """ getball(x::ArbOrRef) = (Arf(midref(x)), Mag(radref(x))) +getball(::Type{Arf}, x::ArbOrRef) = (Arf(midref(x)), Arf(radref(x), prec = precision(x))) getball(::Type{Arb}, x::ArbOrRef) = (Arb(midref(x)), Arb(radref(x), prec = precision(x))) """ diff --git a/test/interval.jl b/test/interval.jl index e02060f0..180072d6 100644 --- a/test/interval.jl +++ b/test/interval.jl @@ -181,13 +181,15 @@ y = one(Arb) Arblib.set!(Arblib.radref(y), 1) - @test getball(x) == getball(Arb, x) == (one(Arf), zero(Mag)) - @test getball(y) == getball(Arb, y) == (one(Arf), one(Mag)) + @test getball(x) == getball(Arf, x) == getball(Arb, x) == (one(Arf), zero(Mag)) + @test getball(y) == getball(Arf, y) == getball(Arb, y) == (one(Arf), one(Mag)) @test getball(x) isa Tuple{Arf,Mag} + @test getball(Arf, x) isa Tuple{Arf,Arf} @test getball(Arb, x) isa Tuple{Arb,Arb} @test precision(getball(Arb(prec = 80))[1]) == 80 + @test precision.(getball(Arf, Arb(prec = 80))) == (80, 80) @test precision.(getball(Arb, Arb(prec = 80))) == (80, 80) end From 1e458501d9dbec8cdbb6f5cbe307ebd51189a0aa Mon Sep 17 00:00:00 2001 From: Joel Dahne Date: Tue, 16 Jan 2024 09:36:45 +0100 Subject: [PATCH 06/16] =?UTF-8?q?Optimize=20set!=20for=20Irrational{:?= =?UTF-8?q?=CF=86}?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/setters.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/setters.jl b/src/setters.jl index 71037c1c..f697a3dd 100644 --- a/src/setters.jl +++ b/src/setters.jl @@ -69,9 +69,9 @@ end function set!(res::ArbLike, ::Irrational{:φ}; prec::Integer = precision(res)) set!(res, 5) - sqrt!(res, res, prec) - add!(res, res, 1, prec) - return div!(res, res, 2, prec) + sqrt!(res, res; prec) + add!(res, res, 1; prec) + return mul_2exp!(res, res, -1) end function set!( From 291405d3cd61733143cf4f23a18b302474ad26c5 Mon Sep 17 00:00:00 2001 From: Joel Dahne Date: Tue, 16 Jan 2024 10:11:22 +0100 Subject: [PATCH 07/16] Add log2 and log10 for Arb and Acb --- src/elementary.jl | 11 +++++++++++ test/elementary.jl | 3 +++ 2 files changed, 14 insertions(+) diff --git a/src/elementary.jl b/src/elementary.jl index b829807c..ccea782a 100644 --- a/src/elementary.jl +++ b/src/elementary.jl @@ -66,6 +66,17 @@ sqrtpos(x::ArbOrRef) = sqrtpos!(zero(x), x) sqrt1pm1(x::ArbOrRef) = sqrt1pm1!(zero(x), x) rsqrt(x::Union{ArbOrRef,AcbOrRef}) = rsqrt!(zero(x), x) +function Base.log2(x::Union{ArbOrRef,AcbOrRef}) + res = log(x) + log_2 = const_log2!(Arb(prec = precision(x))) + return div!(res, res, log_2) +end +function Base.log10(x::Union{ArbOrRef,AcbOrRef}) + res = log(x) + log_10 = const_log10!(Arb(prec = precision(x))) + return div!(res, res, log_10) +end + Base.sinpi(x::Union{ArbOrRef,AcbOrRef}) = sin_pi!(zero(x), x) Base.cospi(x::Union{ArbOrRef,AcbOrRef}) = cos_pi!(zero(x), x) tanpi(x::Union{ArbOrRef,AcbOrRef}) = tan_pi!(zero(x), x) diff --git a/test/elementary.jl b/test/elementary.jl index 00ae477c..a265ab63 100644 --- a/test/elementary.jl +++ b/test/elementary.jl @@ -54,6 +54,9 @@ @test Arblib.rsqrt(T(1 // 4)) == 2 + @test Arblib.contains(2^log2(T(5)), T(5)) + @test Arblib.contains(10^log10(T(5)), T(5)) + @test sinpi(T(1)) == 0 @test cospi(T(1)) == -1 @test Arblib.tanpi(T(1)) == 0 From 46ab2288e597a7c79a659f5235f771f895dc3263 Mon Sep 17 00:00:00 2001 From: Joel Dahne Date: Tue, 16 Jan 2024 10:16:51 +0100 Subject: [PATCH 08/16] Move conversion methods to new file --- src/Arblib.jl | 1 + src/constructors.jl | 8 -- src/conversion.jl | 65 +++++++++++++++ src/float.jl | 56 ------------- test/constructors.jl | 23 ------ test/conversion.jl | 183 +++++++++++++++++++++++++++++++++++++++++++ test/float.jl | 161 ------------------------------------- test/runtests.jl | 1 + 8 files changed, 250 insertions(+), 248 deletions(-) create mode 100644 src/conversion.jl create mode 100644 test/conversion.jl diff --git a/src/Arblib.jl b/src/Arblib.jl index 61e3b3b0..9be775e9 100644 --- a/src/Arblib.jl +++ b/src/Arblib.jl @@ -60,6 +60,7 @@ include("precision.jl") include("setters.jl") include("constructors.jl") +include("conversion.jl") include("predicates.jl") include("show.jl") include("promotion.jl") diff --git a/src/constructors.jl b/src/constructors.jl index 4fc4e2b3..f922eb8d 100644 --- a/src/constructors.jl +++ b/src/constructors.jl @@ -66,14 +66,6 @@ function Acb(re::AbstractString, im::AbstractString; prec::Integer = DEFAULT_PRE return res end -Base.Int(x::ArfOrRef; rnd::Union{arb_rnd,RoundingMode} = RoundNearest) = - is_int(x) ? get_si(x, rnd) : throw(InexactError(:Int64, Int64, x)) - -# TODO: This currently allows construction of Complex{ArbRef}, which -# we probably don't want. -Base.Complex{T}(z::AcbOrRef) where {T} = Complex{T}(realref(z), imagref(z)) -Base.Complex(z::AcbOrRef) = Complex{Arb}(z) - Base.zero(::Union{Mag,Type{Mag}}) = Mag(UInt64(0)) Base.one(::Union{Mag,Type{Mag}}) = Mag(UInt64(1)) Base.zero(x::T) where {T<:Union{Arf,Arb,Acb}} = T(0, prec = precision(x)) diff --git a/src/conversion.jl b/src/conversion.jl new file mode 100644 index 00000000..080cb018 --- /dev/null +++ b/src/conversion.jl @@ -0,0 +1,65 @@ +# Conversion to float types in Base + +## Float64 +Base.Float64(x::MagOrRef, r::RoundingMode = RoundUp) = Float64(x, convert(arb_rnd, r)) +Base.Float64(x::ArfOrRef, r::RoundingMode) = Float64(x, convert(arb_rnd, r)) +Base.Float64(x::ArbOrRef, r::RoundingMode = RoundNearest) = Float64(x, convert(arb_rnd, r)) + +function Base.Float64(x::MagOrRef, r::arb_rnd) + r == ArbRoundUp || + throw(ArgumentError("only supports rounding up when converting Mag to Float64")) + return get(x) +end +Base.Float64(x::ArfOrRef, r::arb_rnd) = get_d(x, r) +Base.Float64(x::ArbOrRef, r::arb_rnd) = Float64(midref(x), r) + +# Deprecated +# TODO: This signature clashes with the above one... +Base.Float64(x::ArfOrRef; rnd::arb_rnd = ArbRoundNearest) = Float64(x, rnd) + +## Float16 and Float32 +Base.Float16(x::MagOrRef, r::RoundingMode = RoundUp) = Float16(Float64(x, r), r) +Base.Float32(x::MagOrRef, r::RoundingMode = RoundUp) = Float32(Float64(x, r), r) +Base.Float16(x::Union{ArfOrRef,ArbOrRef}, r::RoundingMode = RoundNearest) = + Float16(Float64(x, r), r) +Base.Float32(x::Union{ArfOrRef,ArbOrRef}, r::RoundingMode = RoundNearest) = + Float32(Float64(x, r), r) + +## BigFloat + +# Note that this uses different default values than the constructors +# in Base. I always defaults to RoundNearest and uses the precision +# given by the input argument. This means that it doesn't depend on +# the global rounding mode and precision. + +Base.BigFloat(x::Union{ArfOrRef,ArbOrRef}, r::RoundingMode; precision = Base.precision(x)) = + BigFloat(x, convert(Base.MPFR.MPFRRoundingMode, r); precision) + +Base.BigFloat(x::Union{ArfOrRef,ArbOrRef}, r::arb_rnd; precision = Base.precision(x)) = + BigFloat(x, convert(RoundingMode, r); precision) + +function Base.BigFloat( + x::ArfOrRef, + r::Base.MPFR.MPFRRoundingMode = Base.MPFR.RoundNearest; + precision = Base.precision(x), +) + y = BigFloat(; precision) + get!(y, x, r) + return y +end +Base.BigFloat( + x::ArbOrRef, + r::Base.MPFR.MPFRRoundingMode = Base.MPFR.RoundNearest; + precision = Base.precision(x), +) = BigFloat(midref(x), r; precision) + +## Conversion to integers +Base.Int(x::ArfOrRef; rnd::Union{arb_rnd,RoundingMode} = RoundNearest) = + is_int(x) ? get_si(x, rnd) : throw(InexactError(:Int64, Int64, x)) + +## Conversion to Complex + +# TODO: This currently allows construction of Complex{ArbRef}, which +# we probably don't want. +Base.Complex{T}(z::AcbOrRef) where {T} = Complex{T}(realref(z), imagref(z)) +Base.Complex(z::AcbOrRef) = Complex{Arb}(z) diff --git a/src/float.jl b/src/float.jl index e8adb13f..a365cd1e 100644 --- a/src/float.jl +++ b/src/float.jl @@ -1,59 +1,3 @@ -# Conversion to float types in Base - -## Float64 -Base.Float64(x::MagOrRef, r::RoundingMode = RoundUp) = Float64(x, convert(arb_rnd, r)) -Base.Float64(x::ArfOrRef, r::RoundingMode) = Float64(x, convert(arb_rnd, r)) -Base.Float64(x::ArbOrRef, r::RoundingMode = RoundNearest) = Float64(x, convert(arb_rnd, r)) - -function Base.Float64(x::MagOrRef, r::arb_rnd) - r == ArbRoundUp || - throw(ArgumentError("only supports rounding up when converting Mag to Float64")) - return get(x) -end -Base.Float64(x::ArfOrRef, r::arb_rnd) = get_d(x, r) -Base.Float64(x::ArbOrRef, r::arb_rnd) = Float64(midref(x), r) - -# Deprecated -# TODO: This signature clashes with the above one... -Base.Float64(x::ArfOrRef; rnd::arb_rnd = ArbRoundNearest) = Float64(x, rnd) - -## Float16 and Float32 -Base.Float16(x::MagOrRef, r::RoundingMode = RoundUp) = Float16(Float64(x, r), r) -Base.Float32(x::MagOrRef, r::RoundingMode = RoundUp) = Float32(Float64(x, r), r) -Base.Float16(x::Union{ArfOrRef,ArbOrRef}, r::RoundingMode = RoundNearest) = - Float16(Float64(x, r), r) -Base.Float32(x::Union{ArfOrRef,ArbOrRef}, r::RoundingMode = RoundNearest) = - Float32(Float64(x, r), r) - -## BigFloat - -# Note that this uses different default values than the constructors -# in Base. I always defaults to RoundNearest and uses the precision -# given by the input argument. This means that it doesn't depend on -# the global rounding mode and precision. - -Base.BigFloat(x::Union{ArfOrRef,ArbOrRef}, r::RoundingMode; precision = Base.precision(x)) = - BigFloat(x, convert(Base.MPFR.MPFRRoundingMode, r); precision) - -Base.BigFloat(x::Union{ArfOrRef,ArbOrRef}, r::arb_rnd; precision = Base.precision(x)) = - BigFloat(x, convert(RoundingMode, r); precision) - -function Base.BigFloat( - x::ArfOrRef, - r::Base.MPFR.MPFRRoundingMode = Base.MPFR.RoundNearest; - precision = Base.precision(x), -) - y = BigFloat(; precision) - get!(y, x, r) - return y -end -Base.BigFloat( - x::ArbOrRef, - r::Base.MPFR.MPFRRoundingMode = Base.MPFR.RoundNearest; - precision = Base.precision(x), -) = BigFloat(midref(x), r; precision) - -# Common methods in the AbstractFloat interface function eps!(res::ArfOrRef, x::ArfOrRef) isspecial(x) && return nan!(res) return set!(res, set_ulp!(Mag(), x, prec = precision(x))) diff --git a/test/constructors.jl b/test/constructors.jl index a825518c..afd6e392 100644 --- a/test/constructors.jl +++ b/test/constructors.jl @@ -128,29 +128,6 @@ @test isequal(Acb((1, 2), (3, 4)), Acb(Arb((1, 2)), Arb((3, 4)))) end - @testset "Conversion" begin - @testset "Int" begin - @test Int(Arf(2.0)) isa Int - @test Int(Arf(2.0)) == 2 - @test_throws InexactError Int(Arf(2.5)) - - @test Arblib.get_si(Arf(0.5)) == 0 - @test Arblib.get_si(Arf(0.5); rnd = Arblib.ArbRoundFromZero) == 1 - end - - @testset "Complex" begin - @test Complex{Float32}(Acb(2 + 3im)) isa Complex{Float32} - @test Complex{Float64}(Acb(2 + 3im)) isa Complex{Float64} - @test Complex{Arb}(Acb(2 + 3im)) isa Complex{Arb} - @test Complex(Acb(2 + 3im)) isa Complex{Arb} - - @test Complex{Float32}(Acb(2 + 3im)) == 2.0 + 3.0im - @test Complex{Float64}(Acb(2 + 3im)) == 2.0 + 3.0im - @test Complex{Arb}(Acb(2 + 3im)) == 2.0 + 3.0im - @test Complex(Acb(2 + 3im)) == 2.0 + 3.0im - end - end - @testset "zeros/ones" begin for T in [Arf, Arb, Acb] @test zeros(T, 2) == [zero(T), zero(T)] diff --git a/test/conversion.jl b/test/conversion.jl new file mode 100644 index 00000000..e7e06629 --- /dev/null +++ b/test/conversion.jl @@ -0,0 +1,183 @@ +@testset "Conversion" begin + ArbRoundNearest = Arblib.RoundNearest + ArbRoundDown = Arblib.RoundDown + ArbRoundUp = Arblib.RoundUp + + @testset "Floats" begin + @testset "$T" for T in (Float16, Float32, Float64) + # Mag + @test T(Mag(2)) isa T + @test π < T(Mag(π)) == T(Mag(π), RoundUp) == T(Mag(π), ArbRoundUp) < 3.15 + + @test_throws ArgumentError T(Mag(1), RoundDown) + @test_throws ArgumentError T(Mag(1), ArbRoundDown) + @test_throws ArgumentError T(Mag(1), RoundNearest) + @test_throws ArgumentError T(Mag(1), ArbRoundNearest) + + # Arf + @test T(Arf(2)) isa T + @test T(Arf(2)) == 2 + + @test T(1 + Arf(eps(T)) / 3) == T(1) + @test T(1 + Arf(eps(T)) / 3, RoundNearest) == T(1) + @test T(1 + Arf(eps(T)) / 3, ArbRoundNearest) == T(1) + @test T(1 + 2Arf(eps(T)) / 3, RoundNearest) == nextfloat(T(1)) + @test T(1 + 2Arf(eps(T)) / 3, ArbRoundNearest) == nextfloat(T(1)) + @test T(1 + Arf(eps(T)) / 3, RoundDown) == T(1) + @test T(1 + Arf(eps(T)) / 3, ArbRoundDown) == T(1) + @test T(1 + Arf(eps(T)) / 3, RoundUp) == nextfloat(T(1)) + @test T(1 + Arf(eps(T)) / 3, ArbRoundUp) == nextfloat(T(1)) + + # Arb + @test T(Arb(2)) isa T + @test T(Arb(2)) == 2 + + @test T(1 + Arb(eps(T)) / 3) == T(1) + @test T(1 + Arb(eps(T)) / 3, RoundNearest) == T(1) + @test T(1 + Arb(eps(T)) / 3, ArbRoundNearest) == T(1) + @test T(1 + 2Arb(eps(T)) / 3, RoundNearest) == nextfloat(T(1)) + @test T(1 + 2Arb(eps(T)) / 3, ArbRoundNearest) == nextfloat(T(1)) + @test T(1 + Arb(eps(T)) / 3, RoundDown) == T(1) + @test T(1 + Arb(eps(T)) / 3, ArbRoundDown) == T(1) + @test T(1 + Arb(eps(T)) / 3, RoundUp) == nextfloat(T(1)) + @test T(1 + Arb(eps(T)) / 3, ArbRoundUp) == nextfloat(T(1)) + + @test T(setball(Arb, 1 + Arf(eps(T)) / 3, Mag(1))) == T(1) + @test T(setball(Arb, 1 + Arf(eps(T)) / 3, Mag(1)), RoundNearest) == T(1) + @test T(setball(Arb, 1 + Arf(eps(T)) / 3, Mag(1)), ArbRoundNearest) == T(1) + @test T(setball(Arb, 1 + 2Arf(eps(T)) / 3, Mag(1)), RoundNearest) == + nextfloat(T(1)) + @test T(setball(Arb, 1 + 2Arf(eps(T)) / 3, Mag(1)), ArbRoundNearest) == + nextfloat(T(1)) + @test T(setball(Arb, 1 + Arf(eps(T)) / 3, Mag(1)), RoundDown) == T(1) + @test T(setball(Arb, 1 + Arf(eps(T)) / 3, Mag(1)), ArbRoundDown) == T(1) + @test T(setball(Arb, 1 + Arf(eps(T)) / 3, Mag(1)), RoundUp) == nextfloat(T(1)) + @test T(setball(Arb, 1 + Arf(eps(T)) / 3, Mag(1)), ArbRoundUp) == + nextfloat(T(1)) + end + + @testset "BigFloat" begin + @test BigFloat(Arf(2)) isa BigFloat + @test BigFloat(Arf(2)) == 2 + + @test precision(BigFloat(Arf(2), precision = 80)) == 80 + @test precision(BigFloat(Arb(2), precision = 80)) == 80 + + let precision = 80, + ϵ = setprecision(eps(Arf(1, prec = precision)), 256), + M = Base.MPFR + + @test BigFloat(1 + ϵ / 3; precision) == 1 + @test BigFloat(1 + ϵ / 3, RoundNearest; precision) == 1 + @test BigFloat(1 + ϵ / 3, ArbRoundNearest; precision) == 1 + @test BigFloat(1 + ϵ / 3, M.MPFRRoundNearest; precision) == 1 + @test BigFloat(1 + 2ϵ / 3, RoundNearest; precision) == + nextfloat(BigFloat(1; precision)) + @test BigFloat(1 + 2ϵ / 3, ArbRoundNearest; precision) == + nextfloat(BigFloat(1; precision)) + @test BigFloat(1 + 2ϵ / 3, M.MPFRRoundNearest; precision) == + nextfloat(BigFloat(1; precision)) + @test BigFloat(1 + ϵ / 3, RoundDown; precision) == 1 + @test BigFloat(1 + ϵ / 3, ArbRoundDown; precision) == 1 + @test BigFloat(1 + ϵ / 3, M.MPFRRoundDown; precision) == 1 + @test BigFloat(1 + ϵ / 3, RoundUp; precision) == + nextfloat(BigFloat(1; precision)) + @test BigFloat(1 + ϵ / 3, ArbRoundUp; precision) == + nextfloat(BigFloat(1; precision)) + @test BigFloat(1 + ϵ / 3, M.MPFRRoundUp; precision) == + nextfloat(BigFloat(1; precision)) + end + + let precision = 80, + ϵ = setprecision(eps(Arb(1, prec = precision)), 256), + M = Base.MPFR + + @test BigFloat(1 + ϵ / 3; precision) == 1 + @test BigFloat(1 + ϵ / 3, RoundNearest; precision) == 1 + @test BigFloat(1 + ϵ / 3, ArbRoundNearest; precision) == 1 + @test BigFloat(1 + ϵ / 3, M.MPFRRoundNearest; precision) == 1 + @test BigFloat(1 + 2ϵ / 3, RoundNearest; precision) == + nextfloat(BigFloat(1; precision)) + @test BigFloat(1 + 2ϵ / 3, ArbRoundNearest; precision) == + nextfloat(BigFloat(1; precision)) + @test BigFloat(1 + 2ϵ / 3, M.MPFRRoundNearest; precision) == + nextfloat(BigFloat(1; precision)) + @test BigFloat(1 + ϵ / 3, RoundDown; precision) == 1 + @test BigFloat(1 + ϵ / 3, ArbRoundDown; precision) == 1 + @test BigFloat(1 + ϵ / 3, M.MPFRRoundDown; precision) == 1 + @test BigFloat(1 + ϵ / 3, RoundUp; precision) == + nextfloat(BigFloat(1; precision)) + @test BigFloat(1 + ϵ / 3, ArbRoundUp; precision) == + nextfloat(BigFloat(1; precision)) + @test BigFloat(1 + ϵ / 3, M.MPFRRoundUp; precision) == + nextfloat(BigFloat(1; precision)) + end + + let precision = 80, + ϵ = setprecision(eps(Arf(1, prec = precision)), 256), + M = Base.MPFR + + @test BigFloat(setball(Arb, 1 + ϵ / 3, Mag(1)); precision) == 1 + @test BigFloat(setball(Arb, 1 + ϵ / 3, Mag(1)), RoundNearest; precision) == + 1 + @test BigFloat( + setball(Arb, 1 + ϵ / 3, Mag(1)), + ArbRoundNearest; + precision, + ) == 1 + @test BigFloat( + setball(Arb, 1 + ϵ / 3, Mag(1)), + M.MPFRRoundNearest; + precision, + ) == 1 + @test BigFloat(setball(Arb, 1 + 2ϵ / 3, Mag(1)), RoundNearest; precision) == + nextfloat(BigFloat(1; precision)) + @test BigFloat( + setball(Arb, 1 + 2ϵ / 3, Mag(1)), + ArbRoundNearest; + precision, + ) == nextfloat(BigFloat(1; precision)) + @test BigFloat( + setball(Arb, 1 + 2ϵ / 3, Mag(1)), + M.MPFRRoundNearest; + precision, + ) == nextfloat(BigFloat(1; precision)) + @test BigFloat(setball(Arb, 1 + ϵ / 3, Mag(1)), RoundDown; precision) == 1 + @test BigFloat(setball(Arb, 1 + ϵ / 3, Mag(1)), ArbRoundDown; precision) == + 1 + @test BigFloat( + setball(Arb, 1 + ϵ / 3, Mag(1)), + M.MPFRRoundDown; + precision, + ) == 1 + @test BigFloat(setball(Arb, 1 + ϵ / 3, Mag(1)), RoundUp; precision) == + nextfloat(BigFloat(1; precision)) + @test BigFloat(setball(Arb, 1 + ϵ / 3, Mag(1)), ArbRoundUp; precision) == + nextfloat(BigFloat(1; precision)) + @test BigFloat(setball(Arb, 1 + ϵ / 3, Mag(1)), M.MPFRRoundUp; precision) == + nextfloat(BigFloat(1; precision)) + end + end + end + + @testset "Integers" begin + @test Int(Arf(2.0)) isa Int + @test Int(Arf(2.0)) == 2 + @test_throws InexactError Int(Arf(2.5)) + + @test Arblib.get_si(Arf(0.5)) == 0 + @test Arblib.get_si(Arf(0.5); rnd = Arblib.ArbRoundFromZero) == 1 + end + + @testset "Complex" begin + @test Complex{Float32}(Acb(2 + 3im)) isa Complex{Float32} + @test Complex{Float64}(Acb(2 + 3im)) isa Complex{Float64} + @test Complex{Arb}(Acb(2 + 3im)) isa Complex{Arb} + @test Complex(Acb(2 + 3im)) isa Complex{Arb} + + @test Complex{Float32}(Acb(2 + 3im)) == 2.0 + 3.0im + @test Complex{Float64}(Acb(2 + 3im)) == 2.0 + 3.0im + @test Complex{Arb}(Acb(2 + 3im)) == 2.0 + 3.0im + @test Complex(Acb(2 + 3im)) == 2.0 + 3.0im + end +end diff --git a/test/float.jl b/test/float.jl index a9a7fdde..711b6c76 100644 --- a/test/float.jl +++ b/test/float.jl @@ -1,165 +1,4 @@ @testset "float" begin - @testset "Conversion" begin - ArbRoundNearest = Arblib.RoundNearest - ArbRoundDown = Arblib.RoundDown - ArbRoundUp = Arblib.RoundUp - - @testset "$T" for T in (Float16, Float32, Float64) - # Mag - @test T(Mag(2)) isa T - @test π < T(Mag(π)) == T(Mag(π), RoundUp) == T(Mag(π), ArbRoundUp) < 3.15 - - @test_throws ArgumentError T(Mag(1), RoundDown) - @test_throws ArgumentError T(Mag(1), ArbRoundDown) - @test_throws ArgumentError T(Mag(1), RoundNearest) - @test_throws ArgumentError T(Mag(1), ArbRoundNearest) - - # Arf - @test T(Arf(2)) isa T - @test T(Arf(2)) == 2 - - @test T(1 + Arf(eps(T)) / 3) == T(1) - @test T(1 + Arf(eps(T)) / 3, RoundNearest) == T(1) - @test T(1 + Arf(eps(T)) / 3, ArbRoundNearest) == T(1) - @test T(1 + 2Arf(eps(T)) / 3, RoundNearest) == nextfloat(T(1)) - @test T(1 + 2Arf(eps(T)) / 3, ArbRoundNearest) == nextfloat(T(1)) - @test T(1 + Arf(eps(T)) / 3, RoundDown) == T(1) - @test T(1 + Arf(eps(T)) / 3, ArbRoundDown) == T(1) - @test T(1 + Arf(eps(T)) / 3, RoundUp) == nextfloat(T(1)) - @test T(1 + Arf(eps(T)) / 3, ArbRoundUp) == nextfloat(T(1)) - - # Arb - @test T(Arb(2)) isa T - @test T(Arb(2)) == 2 - - @test T(1 + Arb(eps(T)) / 3) == T(1) - @test T(1 + Arb(eps(T)) / 3, RoundNearest) == T(1) - @test T(1 + Arb(eps(T)) / 3, ArbRoundNearest) == T(1) - @test T(1 + 2Arb(eps(T)) / 3, RoundNearest) == nextfloat(T(1)) - @test T(1 + 2Arb(eps(T)) / 3, ArbRoundNearest) == nextfloat(T(1)) - @test T(1 + Arb(eps(T)) / 3, RoundDown) == T(1) - @test T(1 + Arb(eps(T)) / 3, ArbRoundDown) == T(1) - @test T(1 + Arb(eps(T)) / 3, RoundUp) == nextfloat(T(1)) - @test T(1 + Arb(eps(T)) / 3, ArbRoundUp) == nextfloat(T(1)) - - @test T(setball(Arb, 1 + Arf(eps(T)) / 3, Mag(1))) == T(1) - @test T(setball(Arb, 1 + Arf(eps(T)) / 3, Mag(1)), RoundNearest) == T(1) - @test T(setball(Arb, 1 + Arf(eps(T)) / 3, Mag(1)), ArbRoundNearest) == T(1) - @test T(setball(Arb, 1 + 2Arf(eps(T)) / 3, Mag(1)), RoundNearest) == - nextfloat(T(1)) - @test T(setball(Arb, 1 + 2Arf(eps(T)) / 3, Mag(1)), ArbRoundNearest) == - nextfloat(T(1)) - @test T(setball(Arb, 1 + Arf(eps(T)) / 3, Mag(1)), RoundDown) == T(1) - @test T(setball(Arb, 1 + Arf(eps(T)) / 3, Mag(1)), ArbRoundDown) == T(1) - @test T(setball(Arb, 1 + Arf(eps(T)) / 3, Mag(1)), RoundUp) == nextfloat(T(1)) - @test T(setball(Arb, 1 + Arf(eps(T)) / 3, Mag(1)), ArbRoundUp) == - nextfloat(T(1)) - end - - @testset "BigFloat" begin - @test BigFloat(Arf(2)) isa BigFloat - @test BigFloat(Arf(2)) == 2 - - @test precision(BigFloat(Arf(2), precision = 80)) == 80 - @test precision(BigFloat(Arb(2), precision = 80)) == 80 - - let precision = 80, - ϵ = setprecision(eps(Arf(1, prec = precision)), 256), - M = Base.MPFR - - @test BigFloat(1 + ϵ / 3; precision) == 1 - @test BigFloat(1 + ϵ / 3, RoundNearest; precision) == 1 - @test BigFloat(1 + ϵ / 3, ArbRoundNearest; precision) == 1 - @test BigFloat(1 + ϵ / 3, M.MPFRRoundNearest; precision) == 1 - @test BigFloat(1 + 2ϵ / 3, RoundNearest; precision) == - nextfloat(BigFloat(1; precision)) - @test BigFloat(1 + 2ϵ / 3, ArbRoundNearest; precision) == - nextfloat(BigFloat(1; precision)) - @test BigFloat(1 + 2ϵ / 3, M.MPFRRoundNearest; precision) == - nextfloat(BigFloat(1; precision)) - @test BigFloat(1 + ϵ / 3, RoundDown; precision) == 1 - @test BigFloat(1 + ϵ / 3, ArbRoundDown; precision) == 1 - @test BigFloat(1 + ϵ / 3, M.MPFRRoundDown; precision) == 1 - @test BigFloat(1 + ϵ / 3, RoundUp; precision) == - nextfloat(BigFloat(1; precision)) - @test BigFloat(1 + ϵ / 3, ArbRoundUp; precision) == - nextfloat(BigFloat(1; precision)) - @test BigFloat(1 + ϵ / 3, M.MPFRRoundUp; precision) == - nextfloat(BigFloat(1; precision)) - end - - let precision = 80, - ϵ = setprecision(eps(Arb(1, prec = precision)), 256), - M = Base.MPFR - - @test BigFloat(1 + ϵ / 3; precision) == 1 - @test BigFloat(1 + ϵ / 3, RoundNearest; precision) == 1 - @test BigFloat(1 + ϵ / 3, ArbRoundNearest; precision) == 1 - @test BigFloat(1 + ϵ / 3, M.MPFRRoundNearest; precision) == 1 - @test BigFloat(1 + 2ϵ / 3, RoundNearest; precision) == - nextfloat(BigFloat(1; precision)) - @test BigFloat(1 + 2ϵ / 3, ArbRoundNearest; precision) == - nextfloat(BigFloat(1; precision)) - @test BigFloat(1 + 2ϵ / 3, M.MPFRRoundNearest; precision) == - nextfloat(BigFloat(1; precision)) - @test BigFloat(1 + ϵ / 3, RoundDown; precision) == 1 - @test BigFloat(1 + ϵ / 3, ArbRoundDown; precision) == 1 - @test BigFloat(1 + ϵ / 3, M.MPFRRoundDown; precision) == 1 - @test BigFloat(1 + ϵ / 3, RoundUp; precision) == - nextfloat(BigFloat(1; precision)) - @test BigFloat(1 + ϵ / 3, ArbRoundUp; precision) == - nextfloat(BigFloat(1; precision)) - @test BigFloat(1 + ϵ / 3, M.MPFRRoundUp; precision) == - nextfloat(BigFloat(1; precision)) - end - - let precision = 80, - ϵ = setprecision(eps(Arf(1, prec = precision)), 256), - M = Base.MPFR - - @test BigFloat(setball(Arb, 1 + ϵ / 3, Mag(1)); precision) == 1 - @test BigFloat(setball(Arb, 1 + ϵ / 3, Mag(1)), RoundNearest; precision) == - 1 - @test BigFloat( - setball(Arb, 1 + ϵ / 3, Mag(1)), - ArbRoundNearest; - precision, - ) == 1 - @test BigFloat( - setball(Arb, 1 + ϵ / 3, Mag(1)), - M.MPFRRoundNearest; - precision, - ) == 1 - @test BigFloat(setball(Arb, 1 + 2ϵ / 3, Mag(1)), RoundNearest; precision) == - nextfloat(BigFloat(1; precision)) - @test BigFloat( - setball(Arb, 1 + 2ϵ / 3, Mag(1)), - ArbRoundNearest; - precision, - ) == nextfloat(BigFloat(1; precision)) - @test BigFloat( - setball(Arb, 1 + 2ϵ / 3, Mag(1)), - M.MPFRRoundNearest; - precision, - ) == nextfloat(BigFloat(1; precision)) - @test BigFloat(setball(Arb, 1 + ϵ / 3, Mag(1)), RoundDown; precision) == 1 - @test BigFloat(setball(Arb, 1 + ϵ / 3, Mag(1)), ArbRoundDown; precision) == - 1 - @test BigFloat( - setball(Arb, 1 + ϵ / 3, Mag(1)), - M.MPFRRoundDown; - precision, - ) == 1 - @test BigFloat(setball(Arb, 1 + ϵ / 3, Mag(1)), RoundUp; precision) == - nextfloat(BigFloat(1; precision)) - @test BigFloat(setball(Arb, 1 + ϵ / 3, Mag(1)), ArbRoundUp; precision) == - nextfloat(BigFloat(1; precision)) - @test BigFloat(setball(Arb, 1 + ϵ / 3, Mag(1)), M.MPFRRoundUp; precision) == - nextfloat(BigFloat(1; precision)) - end - end - end - @testset "eps" begin @test one(Arf) + eps(Arf) > one(Arf) @test one(Arf) + eps(Arf) / 2 == one(Arf) diff --git a/test/runtests.jl b/test/runtests.jl index 9e83e6a4..ab5d1c60 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -18,6 +18,7 @@ DocMeta.setdocmeta!(Arblib, :DocTestSetup, :(using Arblib); recursive = true) include("manual_overrides.jl") include("setters.jl") include("constructors.jl") + include("conversion.jl") include("predicates.jl") include("show.jl") include("promotion.jl") From a0444415980a3cc8c0e89ad676868614faf20f83 Mon Sep 17 00:00:00 2001 From: Joel Dahne Date: Tue, 16 Jan 2024 10:59:23 +0100 Subject: [PATCH 09/16] Improve conversion to Int and add conversion to BigInt Conversion to Int no longer crashes on overflow. It now also supports Arb and Acb. It removes the rounding argument to conversion to Int. But this didn't do anything anyway since it threw if the input was not an exact integer. --- src/conversion.jl | 38 ++++++++++++++++++++++++++++++++++++-- test/conversion.jl | 36 +++++++++++++++++++++++++++++++----- 2 files changed, 67 insertions(+), 7 deletions(-) diff --git a/src/conversion.jl b/src/conversion.jl index 080cb018..0105ba0d 100644 --- a/src/conversion.jl +++ b/src/conversion.jl @@ -54,8 +54,42 @@ Base.BigFloat( ) = BigFloat(midref(x), r; precision) ## Conversion to integers -Base.Int(x::ArfOrRef; rnd::Union{arb_rnd,RoundingMode} = RoundNearest) = - is_int(x) ? get_si(x, rnd) : throw(InexactError(:Int64, Int64, x)) + +# IMPROVE: We currently don't support any rounding to integers. This +# would maybe be nice to do. +# IMPROVE: Is there any point in supporting conversion to other +# integers? + +function Base.Int(x::ArfOrRef) + is_int(x) || throw(InexactError(:Int, Int, x)) + typemin(Int) <= x <= typemax(Int) || throw(InexactError(:Int, Int, x)) + return get_si(x, ArbRoundNearest) +end + +Base.Int(x::ArbOrRef) = is_int(x) ? Int(midref(x)) : throw(InexactError(:Int, Int, x)) + +Base.Int(x::AcbOrRef) = + is_int(x) ? Int(midref(realref(x))) : throw(InexactError(:Int, Int, x)) + +function Base.BigInt(x::ArfOrRef) + is_int(x) || throw(InexactError(:BigInt, BigInt, x)) + n = fmpz_struct() + ccall( + @libflint(arf_get_fmpz), + Cint, + (Ref{fmpz_struct}, Ref{arf_struct}, Ref{arb_rnd}), + n, + x, + ArbRoundNearest, + ) + return BigInt(n) +end + +Base.BigInt(x::ArbOrRef) = + is_int(x) ? BigInt(midref(x)) : throw(InexactError(:BigInt, BigInt, x)) + +Base.BigInt(x::AcbOrRef) = + is_int(x) ? BigInt(midref(realref(x))) : throw(InexactError(:BigInt, BigInt, x)) ## Conversion to Complex diff --git a/test/conversion.jl b/test/conversion.jl index e7e06629..3b8324ea 100644 --- a/test/conversion.jl +++ b/test/conversion.jl @@ -161,12 +161,38 @@ end @testset "Integers" begin - @test Int(Arf(2.0)) isa Int - @test Int(Arf(2.0)) == 2 - @test_throws InexactError Int(Arf(2.5)) + @testset "Int" begin + @test Int(Arf(2)) isa Int + + @test Int(Arf(2)) == 2 + @test Int(Arf(typemin(Int))) == typemin(Int) + @test Int(Arf(typemax(Int))) == typemax(Int) + + @test_throws InexactError Int(Arf(2.5)) + @test_throws InexactError Int(Arf(typemin(Int)) - 1) + @test_throws InexactError Int(Arf(typemax(Int)) + 1) + + @test Int(Arb(2)) isa Int + @test Int(Arb(2)) == 2 + @test_throws InexactError Int(Arb(2.5)) + @test_throws InexactError Int(setball(Arb, 1, 1)) + + @test Int(Acb(2)) isa Int + @test Int(Acb(2)) == 2 + @test_throws InexactError Int(Acb(2.5)) + @test_throws InexactError Int(Acb(setball(Arb, 1, 1))) + @test_throws InexactError Int(Acb(2, 1)) + end + + @testset "BigInt" begin + @test BigInt(Arf(2)) isa BigInt - @test Arblib.get_si(Arf(0.5)) == 0 - @test Arblib.get_si(Arf(0.5); rnd = Arblib.ArbRoundFromZero) == 1 + @test BigInt(Arf(2)) == 2 + @test BigInt(Arf(typemin(Int)) - 1) == big(typemin(Int)) - 1 + @test BigInt(Arf(typemax(Int)) + 1) == big(typemax(Int)) + 1 + + @test_throws InexactError BigInt(Arf(2.5)) + end end @testset "Complex" begin From ff01d19e2449ea78a77eddb0a1a77e5a94b0e64d Mon Sep 17 00:00:00 2001 From: Joel Dahne Date: Wed, 17 Jan 2024 11:25:12 +0100 Subject: [PATCH 10/16] Remove old TODO note --- src/conversion.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/conversion.jl b/src/conversion.jl index 0105ba0d..708d6350 100644 --- a/src/conversion.jl +++ b/src/conversion.jl @@ -14,7 +14,6 @@ Base.Float64(x::ArfOrRef, r::arb_rnd) = get_d(x, r) Base.Float64(x::ArbOrRef, r::arb_rnd) = Float64(midref(x), r) # Deprecated -# TODO: This signature clashes with the above one... Base.Float64(x::ArfOrRef; rnd::arb_rnd = ArbRoundNearest) = Float64(x, rnd) ## Float16 and Float32 From 272f30978cd897f08261037e2ed96578e4796e30 Mon Sep 17 00:00:00 2001 From: Joel Dahne Date: Thu, 1 Feb 2024 20:38:51 +0100 Subject: [PATCH 11/16] Fix precision for Julia v1.11 The internal single argument version of Base._precision was renamed to Base._precision_with_base_2. --- src/precision.jl | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/precision.jl b/src/precision.jl index 7e07a963..ea99c124 100644 --- a/src/precision.jl +++ b/src/precision.jl @@ -17,16 +17,32 @@ else # overloading that we automatically support giving base argument. # Types - Base._precision(::Type{<:Union{ArbTypes,ArbStructTypes,Ptr{<:ArbStructTypes}}}) = - DEFAULT_PRECISION[] - # Types not storing their precision - Base._precision(::Union{ArbStructTypes,Ptr{<:ArbStructTypes}}) = DEFAULT_PRECISION[] - # Types storing their precision - Base._precision(x::ArbTypes) = x.prec - # Mag doesn't store a precision - Base._precision(::MagOrRef) = DEFAULT_PRECISION[] - # ArbSeries and AcbSeries don't store their precision directly - Base._precision(x::Union{ArbSeries,AcbSeries}) = Base._precision(x.poly) + if VERSION < v"1.11.0-DEV" + Base._precision(::Type{<:Union{ArbTypes,ArbStructTypes,Ptr{<:ArbStructTypes}}}) = + DEFAULT_PRECISION[] + # Types not storing their precision + Base._precision(::Union{ArbStructTypes,Ptr{<:ArbStructTypes}}) = DEFAULT_PRECISION[] + # Types storing their precision + Base._precision(x::ArbTypes) = x.prec + # Mag doesn't store a precision + Base._precision(::MagOrRef) = DEFAULT_PRECISION[] + # ArbSeries and AcbSeries don't store their precision directly + Base._precision(x::Union{ArbSeries,AcbSeries}) = Base._precision(x.poly) + else + Base._precision_with_base_2( + ::Type{<:Union{ArbTypes,ArbStructTypes,Ptr{<:ArbStructTypes}}}, + ) = DEFAULT_PRECISION[] + # Types not storing their precision + Base._precision_with_base_2(::Union{ArbStructTypes,Ptr{<:ArbStructTypes}}) = + DEFAULT_PRECISION[] + # Types storing their precision + Base._precision_with_base_2(x::ArbTypes) = x.prec + # Mag doesn't store a precision + Base._precision_with_base_2(::MagOrRef) = DEFAULT_PRECISION[] + # ArbSeries and AcbSeries don't store their precision directly + Base._precision_with_base_2(x::Union{ArbSeries,AcbSeries}) = + Base._precision_with_base_2(x.poly) + end # Base.precision only allows AbstractFloat, we want to be able to use # all ArbLib types. From f7fab09696625c1f3cc17de18f4778514dc1cc77 Mon Sep 17 00:00:00 2001 From: Joel Dahne Date: Mon, 5 Feb 2024 18:33:17 +0100 Subject: [PATCH 12/16] load_string: New non-inplace version of load_string! --- src/show.jl | 2 ++ test/show.jl | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/show.jl b/src/show.jl index 1fec019b..99cd80f7 100644 --- a/src/show.jl +++ b/src/show.jl @@ -112,6 +112,8 @@ function load_string!(x::Union{MagLike,ArfLike,ArbLike}, str::AbstractString) return x end +load_string(T::Type{<:Union{Mag,Arf,Arb}}, str::AbstractString) = load_string!(zero(T), str) + function dump_string(x::Union{MagLike,ArfLike,ArbLike}) char_ptr = dump(x) str = unsafe_string(char_ptr) diff --git a/test/show.jl b/test/show.jl index 13c6e6cd..d8d79db9 100644 --- a/test/show.jl +++ b/test/show.jl @@ -11,7 +11,9 @@ for x in (Mag(π), Arf(1 // 3), Arb(π)) str = Arblib.dump_string(x) y = Arblib.load_string!(zero(x), str) + z = Arblib.load_string(typeof(x), str) @test isequal(x, y) + @test isequal(x, z) end end From 2d0f6a651edcef8874fed02ea28075b6276d722c Mon Sep 17 00:00:00 2001 From: Joel Dahne Date: Mon, 5 Feb 2024 18:42:08 +0100 Subject: [PATCH 13/16] Set version to 1.1 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index e19c7eff..9ba3a075 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Arblib" uuid = "fb37089c-8514-4489-9461-98f9c8763369" authors = ["Marek Kaluba ", "Sascha Timme ", "Joel Dahne "] -version = "1.0" +version = "1.1" [deps] FLINT_jll = "e134572f-a0d5-539d-bddf-3cad8db41a82" From 4c86bf569284f727545b74595ad67153a6ae58ad Mon Sep 17 00:00:00 2001 From: Joel Dahne Date: Mon, 5 Feb 2024 21:14:28 +0100 Subject: [PATCH 14/16] Fix conversion tests with rounding There was a typo in the conversion tests that meant there were no tests with Arblib.arb_rnd. Fixing this lead to discovering that there is no conversion from arb_rnd to RoundingMode, which has now been added. --- src/conversion.jl | 6 ++++++ src/rounding.jl | 19 +++++++++++++++++++ test/conversion.jl | 24 ++++++++++++++++++++---- test/rounding.jl | 15 +++++++++++++++ test/runtests.jl | 1 + 5 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 test/rounding.jl diff --git a/src/conversion.jl b/src/conversion.jl index 708d6350..cc4e1cf0 100644 --- a/src/conversion.jl +++ b/src/conversion.jl @@ -18,11 +18,17 @@ Base.Float64(x::ArfOrRef; rnd::arb_rnd = ArbRoundNearest) = Float64(x, rnd) ## Float16 and Float32 Base.Float16(x::MagOrRef, r::RoundingMode = RoundUp) = Float16(Float64(x, r), r) +Base.Float16(x::MagOrRef, r::arb_rnd) = Float16(Float64(x, r), convert(RoundingMode, r)) Base.Float32(x::MagOrRef, r::RoundingMode = RoundUp) = Float32(Float64(x, r), r) +Base.Float32(x::MagOrRef, r::arb_rnd) = Float32(Float64(x, r), convert(RoundingMode, r)) Base.Float16(x::Union{ArfOrRef,ArbOrRef}, r::RoundingMode = RoundNearest) = Float16(Float64(x, r), r) +Base.Float16(x::Union{ArfOrRef,ArbOrRef}, r::arb_rnd) = + Float16(Float64(x, r), convert(RoundingMode, r)) Base.Float32(x::Union{ArfOrRef,ArbOrRef}, r::RoundingMode = RoundNearest) = Float32(Float64(x, r), r) +Base.Float32(x::Union{ArfOrRef,ArbOrRef}, r::arb_rnd) = + Float32(Float64(x, r), convert(RoundingMode, r)) ## BigFloat diff --git a/src/rounding.jl b/src/rounding.jl index b9a6dc90..fa6de69a 100644 --- a/src/rounding.jl +++ b/src/rounding.jl @@ -28,3 +28,22 @@ Base.convert(::Type{arb_rnd}, ::RoundingMode{:Down}) = ArbRoundDown Base.convert(::Type{arb_rnd}, ::RoundingMode{:Up}) = ArbRoundUp Base.convert(::Type{arb_rnd}, ::RoundingMode{:Nearest}) = ArbRoundNearest Base.convert(::Type{arb_rnd}, ::RoundingMode{:Exact}) = ArbRoundExact + +function Base.convert(::Type{RoundingMode}, r::arb_rnd) + if r == ArbRoundToZero + return RoundToZero + elseif r == ArbRoundFromZero + return RoundFromZero + elseif r == ArbRoundDown + return RoundDown + elseif r == ArbRoundUp + return RoundUp + elseif r == ArbRoundNearest + return RoundNearest + elseif r == ArbRoundExact + # No RoundExact, we fall back to RoundNearest + return RoundNearest + else + throw(ArgumentError("invalid Arb rounding mode code: $r")) + end +end diff --git a/test/conversion.jl b/test/conversion.jl index 3b8324ea..3fd84d0e 100644 --- a/test/conversion.jl +++ b/test/conversion.jl @@ -1,7 +1,7 @@ @testset "Conversion" begin - ArbRoundNearest = Arblib.RoundNearest - ArbRoundDown = Arblib.RoundDown - ArbRoundUp = Arblib.RoundUp + ArbRoundNearest = Arblib.ArbRoundNearest + ArbRoundDown = Arblib.ArbRoundDown + ArbRoundUp = Arblib.ArbRoundUp @testset "Floats" begin @testset "$T" for T in (Float16, Float32, Float64) @@ -186,12 +186,28 @@ @testset "BigInt" begin @test BigInt(Arf(2)) isa BigInt - @test BigInt(Arf(2)) == 2 @test BigInt(Arf(typemin(Int)) - 1) == big(typemin(Int)) - 1 @test BigInt(Arf(typemax(Int)) + 1) == big(typemax(Int)) + 1 @test_throws InexactError BigInt(Arf(2.5)) + + @test BigInt(Arb(2)) isa BigInt + @test BigInt(Arb(2)) == 2 + @test BigInt(Arb(typemin(Int)) - 1) == big(typemin(Int)) - 1 + @test BigInt(Arb(typemax(Int)) + 1) == big(typemax(Int)) + 1 + + @test_throws InexactError BigInt(Arb(2.5)) + @test_throws InexactError BigInt(setball(Arb, 1, 1)) + + @test BigInt(Acb(2)) isa BigInt + @test BigInt(Acb(2)) == 2 + @test BigInt(Acb(typemin(Int)) - 1) == big(typemin(Int)) - 1 + @test BigInt(Acb(typemax(Int)) + 1) == big(typemax(Int)) + 1 + + @test_throws InexactError BigInt(Acb(2.5)) + @test_throws InexactError BigInt(Acb(setball(Arb, 1, 1))) + @test_throws InexactError BigInt(Acb(2, 1)) end end diff --git a/test/rounding.jl b/test/rounding.jl new file mode 100644 index 00000000..a4c271a8 --- /dev/null +++ b/test/rounding.jl @@ -0,0 +1,15 @@ +@testset "rounding" begin + @test convert(Arblib.arb_rnd, RoundToZero) == Arblib.ArbRoundToZero + @test convert(Arblib.arb_rnd, RoundFromZero) == Arblib.ArbRoundFromZero + @test convert(Arblib.arb_rnd, RoundDown) == Arblib.ArbRoundDown + @test convert(Arblib.arb_rnd, RoundUp) == Arblib.ArbRoundUp + @test convert(Arblib.arb_rnd, RoundNearest) == Arblib.ArbRoundNearest + @test convert(Arblib.arb_rnd, RoundingMode{:Exact}()) == Arblib.ArbRoundExact + + @test convert(RoundingMode, Arblib.ArbRoundToZero) == RoundToZero + @test convert(RoundingMode, Arblib.ArbRoundFromZero) == RoundFromZero + @test convert(RoundingMode, Arblib.ArbRoundDown) == RoundDown + @test convert(RoundingMode, Arblib.ArbRoundUp) == RoundUp + @test convert(RoundingMode, Arblib.ArbRoundNearest) == RoundNearest + @test convert(RoundingMode, Arblib.ArbRoundExact) == RoundNearest +end diff --git a/test/runtests.jl b/test/runtests.jl index ab5d1c60..c6adca5b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -11,6 +11,7 @@ DocMeta.setdocmeta!(Arblib, :DocTestSetup, :(using Arblib); recursive = true) include("ArbCall/runtests.jl") include("arb_types.jl") + include("rounding.jl") include("types.jl") include("hash.jl") include("serialize.jl") From ecebae9df22345c2e22d2bab167ab4091122a6ed Mon Sep 17 00:00:00 2001 From: Joel Dahne Date: Tue, 6 Feb 2024 08:59:56 +0100 Subject: [PATCH 15/16] convert ArbRoundExact to RoundingMode{:Exact} instead of RoundNearest --- src/rounding.jl | 3 +-- test/rounding.jl | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rounding.jl b/src/rounding.jl index fa6de69a..303312e5 100644 --- a/src/rounding.jl +++ b/src/rounding.jl @@ -41,8 +41,7 @@ function Base.convert(::Type{RoundingMode}, r::arb_rnd) elseif r == ArbRoundNearest return RoundNearest elseif r == ArbRoundExact - # No RoundExact, we fall back to RoundNearest - return RoundNearest + return RoundingMode{:Exact}() else throw(ArgumentError("invalid Arb rounding mode code: $r")) end diff --git a/test/rounding.jl b/test/rounding.jl index a4c271a8..9370e65e 100644 --- a/test/rounding.jl +++ b/test/rounding.jl @@ -11,5 +11,5 @@ @test convert(RoundingMode, Arblib.ArbRoundDown) == RoundDown @test convert(RoundingMode, Arblib.ArbRoundUp) == RoundUp @test convert(RoundingMode, Arblib.ArbRoundNearest) == RoundNearest - @test convert(RoundingMode, Arblib.ArbRoundExact) == RoundNearest + @test convert(RoundingMode, Arblib.ArbRoundExact) == RoundingMode{:Exact}() end From 8e5abcdd1950dc7d58af70cd1bc645959575d07d Mon Sep 17 00:00:00 2001 From: Joel Dahne Date: Tue, 6 Feb 2024 14:10:47 +0100 Subject: [PATCH 16/16] Simplify conversion to Float16 and Float32 --- src/conversion.jl | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/conversion.jl b/src/conversion.jl index cc4e1cf0..a0cc000b 100644 --- a/src/conversion.jl +++ b/src/conversion.jl @@ -18,22 +18,20 @@ Base.Float64(x::ArfOrRef; rnd::arb_rnd = ArbRoundNearest) = Float64(x, rnd) ## Float16 and Float32 Base.Float16(x::MagOrRef, r::RoundingMode = RoundUp) = Float16(Float64(x, r), r) -Base.Float16(x::MagOrRef, r::arb_rnd) = Float16(Float64(x, r), convert(RoundingMode, r)) +Base.Float16(x::MagOrRef, r::arb_rnd) = Float16(x, convert(RoundingMode, r)) Base.Float32(x::MagOrRef, r::RoundingMode = RoundUp) = Float32(Float64(x, r), r) -Base.Float32(x::MagOrRef, r::arb_rnd) = Float32(Float64(x, r), convert(RoundingMode, r)) +Base.Float32(x::MagOrRef, r::arb_rnd) = Float32(x, convert(RoundingMode, r)) Base.Float16(x::Union{ArfOrRef,ArbOrRef}, r::RoundingMode = RoundNearest) = Float16(Float64(x, r), r) -Base.Float16(x::Union{ArfOrRef,ArbOrRef}, r::arb_rnd) = - Float16(Float64(x, r), convert(RoundingMode, r)) +Base.Float16(x::Union{ArfOrRef,ArbOrRef}, r::arb_rnd) = Float16(x, convert(RoundingMode, r)) Base.Float32(x::Union{ArfOrRef,ArbOrRef}, r::RoundingMode = RoundNearest) = Float32(Float64(x, r), r) -Base.Float32(x::Union{ArfOrRef,ArbOrRef}, r::arb_rnd) = - Float32(Float64(x, r), convert(RoundingMode, r)) +Base.Float32(x::Union{ArfOrRef,ArbOrRef}, r::arb_rnd) = Float32(x, convert(RoundingMode, r)) ## BigFloat # Note that this uses different default values than the constructors -# in Base. I always defaults to RoundNearest and uses the precision +# in Base. It always defaults to RoundNearest and uses the precision # given by the input argument. This means that it doesn't depend on # the global rounding mode and precision.