Skip to content

Commit

Permalink
don't mutate globals when constructing Rational from AbstractIrrational
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nsajko committed Nov 23, 2024
1 parent 0bedaae commit b72a316
Showing 1 changed file with 30 additions and 7 deletions.
37 changes: 30 additions & 7 deletions base/irrationals.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down

0 comments on commit b72a316

Please sign in to comment.