From b72a316f30ecb5eef15450dabb94abc928e44d39 Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Tue, 24 Sep 2024 07:39:55 +0200 Subject: [PATCH 1/2] don't mutate globals when constructing Rational from AbstractIrrational Relying on `ScopedValues`, set `BigFloat` precision without mutating the global default, while constructing `Rational` from `AbstractIrrational`. Also helps avoid reading the global defaults for the precision and rounding mode, together with #56095. --- base/irrationals.jl | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/base/irrationals.jl b/base/irrationals.jl index 76222997865c0..963b480fbf9af 100644 --- a/base/irrationals.jl +++ b/base/irrationals.jl @@ -60,20 +60,43 @@ AbstractFloat(x::AbstractIrrational) = Float64(x)::Float64 Float16(x::AbstractIrrational) = Float16(Float32(x)::Float32) Complex{T}(x::AbstractIrrational) where {T<:Real} = Complex{T}(T(x)) -function _irrational_to_rational(::Type{T}, x::AbstractIrrational) where T<:Integer - o = precision(BigFloat) +function _irrational_to_rational_at_current_precision(::Type{T}, x::AbstractIrrational) where {T <: Integer} + bx = BigFloat(x) + r = rationalize(T, bx, tol = 0) + br = BigFloat(r, precision = precision(BigFloat) + 32) + if eps(bx) < abs(br - bx) + r + else + nothing # Error is too small, repeat with greater precision. + end +end +function _irrational_to_rational_at_precision(::Type{T}, x::AbstractIrrational, p::Int) where {T <: Integer} + f = let x = x + () -> _irrational_to_rational_at_current_precision(T, x) + end + setprecision(f, BigFloat, p) +end +function _irrational_to_rational_at_current_rounding_mode(::Type{T}, x::AbstractIrrational) where {T <: Integer} + if T <: BigInt + _throw_argument_error_irrational_to_rational_bigint() # avoid infinite loop + end p = 256 while true - setprecision(BigFloat, p) - bx = BigFloat(x) - r = rationalize(T, bx, tol=0) - if abs(BigFloat(r) - bx) > eps(bx) - setprecision(BigFloat, o) + r = _irrational_to_rational_at_precision(T, x, p) + if r isa Number return r end p += 32 end end +function _irrational_to_rational( + ::Type{T}, x::AbstractIrrational, +) where {T <: Integer} + f = let x = x + () -> _irrational_to_rational_at_current_rounding_mode(T, x) + end + setrounding(f, BigFloat, RoundNearest) +end Rational{T}(x::AbstractIrrational) where {T<:Integer} = _irrational_to_rational(T, x) _throw_argument_error_irrational_to_rational_bigint() = throw(ArgumentError("Cannot convert an AbstractIrrational to a Rational{BigInt}: use rationalize(BigInt, x) instead")) Rational{BigInt}(::AbstractIrrational) = _throw_argument_error_irrational_to_rational_bigint() From a124648b6e92b325fd8e65e85e5f0cc03f117a70 Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Sat, 23 Nov 2024 09:22:41 +0100 Subject: [PATCH 2/2] less lines --- base/irrationals.jl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/base/irrationals.jl b/base/irrationals.jl index 963b480fbf9af..fc7d6eca732eb 100644 --- a/base/irrationals.jl +++ b/base/irrationals.jl @@ -89,9 +89,7 @@ function _irrational_to_rational_at_current_rounding_mode(::Type{T}, x::Abstract p += 32 end end -function _irrational_to_rational( - ::Type{T}, x::AbstractIrrational, -) where {T <: Integer} +function _irrational_to_rational(::Type{T}, x::AbstractIrrational) where {T <: Integer} f = let x = x () -> _irrational_to_rational_at_current_rounding_mode(T, x) end