Skip to content

Commit e2cfacb

Browse files
committed
fixes for constructing a Rational from an AbstractIrrational
Fixes several issues: * Set `BigFloat` precision without mutating the global default, relying on the new ScopedValues functionality. * Avoid reading the global defaults for the precision and rounding mode, relies on #56095. * Try to ensure the loop terminates in a reasonable amount of time, and without trying to allocate excessively large `BigFloat` values.
1 parent a007e80 commit e2cfacb

File tree

1 file changed

+41
-10
lines changed

1 file changed

+41
-10
lines changed

base/irrationals.jl

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,19 +51,50 @@ AbstractFloat(x::AbstractIrrational) = Float64(x)::Float64
5151
Float16(x::AbstractIrrational) = Float16(Float32(x)::Float32)
5252
Complex{T}(x::AbstractIrrational) where {T<:Real} = Complex{T}(T(x))
5353

54-
function _irrational_to_rational(::Type{T}, x::AbstractIrrational) where T<:Integer
55-
o = precision(BigFloat)
56-
p = 256
57-
while true
58-
setprecision(BigFloat, p)
59-
bx = BigFloat(x)
60-
r = rationalize(T, bx, tol=0)
61-
if abs(BigFloat(r) - bx) > eps(bx)
62-
setprecision(BigFloat, o)
54+
function _irrational_to_rational_at_current_precision(
55+
::Type{T}, x::AbstractIrrational,
56+
) where {T <: Integer}
57+
bx = BigFloat(x)
58+
r = rationalize(T, bx, tol = 0)
59+
br_func = let r = r
60+
() -> BigFloat(r)
61+
end
62+
br = setprecision(br_func, BigFloat, precision(BigFloat) + 32)
63+
if eps(bx) < abs(br - bx)
64+
r
65+
else
66+
nothing # Error is too small, repeat with greater precision.
67+
end
68+
end
69+
function _irrational_to_rational_at_precision(
70+
::Type{T}, x::AbstractIrrational, p::Int,
71+
) where {T <: Integer}
72+
f = let x = x
73+
() -> _irrational_to_rational_at_current_precision(T, x)
74+
end
75+
setprecision(f, BigFloat, p)
76+
end
77+
function _irrational_to_rational_at_current_rounding_mode(
78+
::Type{T}, x::AbstractIrrational, precisions,
79+
) where {T <: Integer}
80+
if T <: BigInt
81+
_throw_argument_error_irrational_to_rational_bigint()
82+
end
83+
for p precisions
84+
r = _irrational_to_rational_at_precision(T, x, p)
85+
if r isa Number
6386
return r
6487
end
65-
p += 32
6688
end
89+
throw(ArgumentError("failed to rationalize irrational"))
90+
end
91+
function _irrational_to_rational(
92+
::Type{T}, x::AbstractIrrational, precisions = 256:32:16777216,
93+
) where {T <: Integer}
94+
f = let x = x, precisions = precisions
95+
() -> _irrational_to_rational_at_current_rounding_mode(T, x, precisions)
96+
end
97+
setrounding(f, BigFloat, RoundNearest)
6798
end
6899
Rational{T}(x::AbstractIrrational) where {T<:Integer} = _irrational_to_rational(T, x)
69100
_throw_argument_error_irrational_to_rational_bigint() = throw(ArgumentError("Cannot convert an AbstractIrrational to a Rational{BigInt}: use rationalize(BigInt, x) instead"))

0 commit comments

Comments
 (0)