Skip to content

Commit 6b51cb7

Browse files
committed
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 JuliaLang#56095. What does this fix: * in the case of the `Irrational` constants defined in `MathConstants`: relevant methods have `@assume_effects :foldable` applied, which includes `:effect_free`, which requires that no globals be mutated (followup on JuliaLang#55886) * in the case of `AbstractIrrational` values in general, this PR prevents data races on the global `BigFloat` precision
1 parent fa3ca0f commit 6b51cb7

File tree

1 file changed

+27
-7
lines changed

1 file changed

+27
-7
lines changed

base/irrationals.jl

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,20 +60,40 @@ AbstractFloat(x::AbstractIrrational) = Float64(x)::Float64
6060
Float16(x::AbstractIrrational) = Float16(Float32(x)::Float32)
6161
Complex{T}(x::AbstractIrrational) where {T<:Real} = Complex{T}(T(x))
6262

63-
function _irrational_to_rational(::Type{T}, x::AbstractIrrational) where T<:Integer
64-
o = precision(BigFloat)
63+
function _irrational_to_rational_at_current_precision(::Type{T}, x::AbstractIrrational) where {T <: Integer}
64+
bx = BigFloat(x)
65+
r = rationalize(T, bx, tol = 0)
66+
if abs(BigFloat(r) - bx) > eps(bx)
67+
r
68+
else
69+
nothing # Error is too small, repeat with greater precision.
70+
end
71+
end
72+
function _irrational_to_rational_at_precision(::Type{T}, x::AbstractIrrational, p::Int) where {T <: Integer}
73+
f = let x = x
74+
() -> _irrational_to_rational_at_current_precision(T, x)
75+
end
76+
setprecision(f, BigFloat, p)
77+
end
78+
function _irrational_to_rational_at_current_rounding_mode(::Type{T}, x::AbstractIrrational) where {T <: Integer}
79+
if T <: BigInt
80+
_throw_argument_error_irrational_to_rational_bigint() # avoid infinite loop
81+
end
6582
p = 256
6683
while true
67-
setprecision(BigFloat, p)
68-
bx = BigFloat(x)
69-
r = rationalize(T, bx, tol=0)
70-
if abs(BigFloat(r) - bx) > eps(bx)
71-
setprecision(BigFloat, o)
84+
r = _irrational_to_rational_at_precision(T, x, p)
85+
if r isa Number
7286
return r
7387
end
7488
p += 32
7589
end
7690
end
91+
function _irrational_to_rational(::Type{T}, x::AbstractIrrational) where {T <: Integer}
92+
f = let x = x
93+
() -> _irrational_to_rational_at_current_rounding_mode(T, x)
94+
end
95+
setrounding(f, BigFloat, RoundNearest)
96+
end
7797
Rational{T}(x::AbstractIrrational) where {T<:Integer} = _irrational_to_rational(T, x)
7898
_throw_argument_error_irrational_to_rational_bigint() = throw(ArgumentError("Cannot convert an AbstractIrrational to a Rational{BigInt}: use rationalize(BigInt, x) instead"))
7999
Rational{BigInt}(::AbstractIrrational) = _throw_argument_error_irrational_to_rational_bigint()

0 commit comments

Comments
 (0)